-1

This does not compile under Microsoft using cl (error msg below - what is it complaining about?) but it does using gcc. Can someone explain in plain English the following line? Is there some code with less pointer logic that would do as well?

============================================================

*(char *)(new_node->data + i) = *(char *)(new_data + i);

============================================================

/* Function to add a node at the beginning of Linked List. 
This function expects a pointer to the data to be added 
and size of the data type */
void push(struct Node** head_ref, void *new_data, size_t data_size) 
{ 
    // Allocate memory for node 
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node)); 

    new_node->data = malloc(data_size); 
    new_node->next = (*head_ref); 

    // Copy contents of new_data to newly allocated memory. 
    // Assumption: char takes 1 byte. 
    int i;                                        //line 27
    for (i=0; i<data_size; i++)                   //line 28
        *(char *)(new_node->data + i) = *(char *)(new_data + i); 

    // Change head pointer as new node is added at the beginning 
    (*head_ref) = new_node; 
} 

MS error messages

C:\Temp\c\LinkedLists>cl z.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

z.c
z.c(27) : error C2143: syntax error : missing ';' before 'type'
z.c(28) : error C2065: 'i' : undeclared identifier
z.c(28) : error C2065: 'i' : undeclared identifier
z.c(28) : error C2065: 'i' : undeclared identifier
z.c(29) : error C2065: 'i' : undeclared identifier
z.c(29) : error C2036: 'void *' : unknown size
z.c(29) : error C2065: 'i' : undeclared identifier
z.c(29) : error C2036: 'void *' : unknown size
z.c(29) : error C2106: '=' : left operand must be l-value
z.c(74) : error C2143: syntax error : missing ';' before 'type'
z.c(76) : error C2143: syntax error : missing ';' before 'type'
z.c(78) : error C2065: 'arr2' : undeclared identifier
z.c(78) : error C2109: subscript requires array or pointer type
z.c(78) : error C2065: 'float_size' : undeclared identifier
z.c(78) : warning C4022: 'push' : pointer mismatch for actual parameter 2
z.c(78) : error C2198: 'push' : too few arguments for call
WillardF
  • 13
  • 3
  • Maybe: [Pointer arithmetic for void pointer in C](//stackoverflow.com/a/3524270) – 001 Nov 11 '19 at 18:11
  • If you have not mastered programming to the point that you do not get compiler errors anymore, then instead of attacking the non-trivial topic of pointers for making linked lists, you should probably heed the idea https://ericlippert.com/2014/03/21/find-a-simpler-problem/ – Yunnosch Nov 11 '19 at 18:44
  • Don't cast the result of a call to `malloc()` etc. - it is unnecessary and can hide the very real problem of a missing prototype. – mlp Nov 11 '19 at 19:23

1 Answers1

0

Here

for (i=0; i<data_size; i++)
{
    *(char *)(new_node->data + i) = *(char *)(new_data + i);

    /* same code again, with ascii-art comment labels...*/

        *      (char *) (       new_node->      data          + i         )
    /*  ^deref  cast      (in pointed struct)  pointer    moving offset     */
    /*  v         v               ---             v             v           */
    =   *      (char *) (                      new_data       + i         );
}

we have the pointer to the data needing to be stored in the new node, new_data, of type pointer to void.

The variable i is increasing to cover the range 0...data_size-1.

So new_data + i is pointing, step by step, to memory locations, starting at the data pointer and walking through all of the data stored there. In the summing of i (that is (new_data + i)), effectively the size of void multiplied by i is added to the void pointer (and not the size of char, which I think the code tried; luckily char and void are of the same size).

The result of the summation is cast to pointer to char(that is the part (char *)), and dereferenced (that is the lonely *), with the effect that the right side of the = is accessing input data, char by char.

The left side of the = is similar, just working on the pointer to the recently allocated memory for the data to be stored (which is referenced from the struct, but not contained in it). That allocated memory is filled, char by char, with the input data referenced by the input data pointer.

In both cases it is "calculate something, force it to be understood as a pointer to a character, then dereference it".

The goal (to be able to store arbitrary data of unknown type in nodes of a linked list) is always messy and any way to avoid this ugly pointer arithmetic would probably be even more ugly, because of being misleading (e.g. using array syntax for things which are not actually arrays...).

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • Thanks Yunnosch. I get that it's moving one character at a time. I just can't untangle the syntax. I take the first * to be a dereference indicator but don't know how to read the rest. Can you put it in words? Also, scuse my ignorance but isn't there a mass move method (strcopy or something like that) that would be better than byte at a time? Thanks. – WillardF Nov 11 '19 at 23:14
  • Hi. I thought I did, but I added minimal code quotes to make the references clear. The mass move function would probably be `memcpy` (https://en.cppreference.com/w/c/string/byte/memcpy) but that is not the question. – Yunnosch Nov 11 '19 at 23:20
  • You might find this, another of my answers on `void*`, interesting: https://stackoverflow.com/a/58281061 – Yunnosch Nov 11 '19 at 23:36
  • Thanks again Yunnosch. Don't know why it took so long for me to see (char *) as a cast. I admire the care you take in your responses to this and other questions. – WillardF Nov 12 '19 at 17:30