0

The code that replaces the head:


void push(newnode ** head, int val) 
{
    newnode * new_node;
    new_node = (newnode *) malloc(sizeof(newnode));

    new_node->value = val;
    new_node->next = *head;
    *head = new_node;
}

// Defining head in main()
int main() {
    newnode* head = (newnode*) malloc(sizeof(newnode));
    //Calling push():
    addnode(*head, 4); 
    /* I assumed since push takes a newnode** argument, that head 
    should have an additional asterisk prefixed to it. */
}


The struct I'm using to create nodes:

typedef struct node
{
    int value;
    struct node* next;
} newnode;

head is the head of the linked list; the 1st node. I don't understand why we de-reference head in the argument list for the push function and what I'm Actually doing in the last line and the one before it in the function.

I tried experimenting with the code a little to understand it but I still didn't really get it...

godhelpme
  • 1
  • 1
  • Welcome to SO. Please do not add unrelated tags. That is considered spamming. There are no arrays or databases involved at all. – Gerhardh Dec 09 '22 at 07:35
  • When it comes to linked lists it is a good idea to start doing it on paper. Draw your nodes and pointers and follow what is going on in the function. – Gerhardh Dec 09 '22 at 07:36
  • Sorry about the tags, it forced me to use 5 tags minimum and I already covered what is actually in the question lol. Also, I've tried drawing the function's operations on paper but in terms of what's happening in the computer, I don't understand why we de-reference the head in the arguments for the function. If head's a pointer to a node, then *head would mean that it's the actual node. From there, I'm completely lost. Why do I need to access the node itself and not it's address? – godhelpme Dec 09 '22 at 07:47
  • It allows you to use 5 tags maximum. No minimum. – Gerhardh Dec 09 '22 at 07:54
  • The post button didn't appear until I put the 5th tag in so Idk. – godhelpme Dec 09 '22 at 07:55
  • You missed one `*` in your parameter list. You have a pointer to a pointer. `*head =` does not access the struct but a pointer to it. That is how pointers are modified within functions. If you only change `head` you will change a local copy that is not visible after returning from the function. If you draw the `node_t **head` correct, that should also match your pen&paper model. – Gerhardh Dec 09 '22 at 07:55
  • But if head is a pointer value, then shouldn't the argument receive the actual head of the list and be able to access the next nodes of the list from there? The way I see it, head is a pointer to an address, which holds a node which holds a pointer to the next node, therefore if we pass a function the same address it should be able to access those same elements of the node that's IN that address, no? – godhelpme Dec 09 '22 at 08:01
  • It might help to think of a LL as being akin to a stack... Although there are 'techniques' to do other things, in the simplest case (as you've got), the newest node is always 'pushed' onto the top of the stack (it becomes the newest head.) To delete nodes (in sequence) from a stack, the 'head' is popped (isolated), making its successor the new head... It's a rough analogy, but it may help you understand... – Fe2O3 Dec 09 '22 at 08:02
  • No. `*head` does not access a node. It accesses a pointer to your node. – Gerhardh Dec 09 '22 at 08:03
  • in main, head is defined in the following line: newnode* head; while newnode is the typedef to create a new node so when I take newnode** head as an argument, what does that actually mean? – godhelpme Dec 09 '22 at 08:15
  • Don't describe your code. Show it. How do you call your function? What is `newnode`? – Gerhardh Dec 09 '22 at 08:28
  • newnode is the type def I gave in my original post; the struct I use to declare a newnode in the LL. int main() { newnode* head = (newnode*) malloc(sizeof(newnode)); addnode(*head, 4); } Those're the 2 lines that are relevant to the question... I got the code from learn-c.org right and they don't show what arguments they pass to the function so I just assumed since head is a pointer and push receives a double pointer, I passed a double pointer of *head.. – godhelpme Dec 09 '22 at 08:37
  • Please add it to your question. Code in comments is unreadable. – Gerhardh Dec 09 '22 at 08:50
  • My bad... Didn't realise. Added it to the OP. – godhelpme Dec 09 '22 at 09:16
  • Update: node_t is actually just newnode, which is the structure I use for adding a new node. Updated it in the question. – godhelpme Dec 09 '22 at 09:27
  • `addnode(*head, 4);` What does your compiler say about that? That's a type mismatch. You need `addnode(&head, 4);` (Assuming `addnode` should actually be your `push` function) – Gerhardh Dec 09 '22 at 11:25
  • Oh yea, I forgot to change that bit. I was mainly working on pen&paper so I didnt change the code around too much.. Thanks for the help today! – godhelpme Dec 09 '22 at 11:49

2 Answers2

1
void push(newnode ** head, int val) 
{
    newnode * new_node;
    new_node = (newnode *) malloc(sizeof(newnode));

This allocates a new node and makes new_node point to it.

    new_node->val = val;

This sets the new node's value to the value we want to add.

    new_node->next = *head;

This sets the next node after the new node to be the node that the head pointer currently points to, that is, the current first node.

    *head = new_node;

This makes the head pointer point to the new node, making the new node the first node in the list.

}
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • 2
    It took an entire CTO of a multi-billion dollar company to help me understand this lmfaoooo. Thanks a lot man, you're the best. And also huge thanks to Gerhardh for trying his best with my beginner self. – godhelpme Dec 09 '22 at 09:41
  • 1
    My insomnia is your gain. ;) – David Schwartz Dec 09 '22 at 09:53
1

First: some issues in your code:

    newnode* head = (newnode*) malloc(sizeof(newnode));

This allocates a node, but leaves its members uninitialised. This is not good, as iterating such a list will lead to undefined behavior when the head->next is used for potentially finding a next node. It must be set to NULL. And as we are at it, there is no good reason to leave the node's value uninitialised either.

Secondly, this malloc duplicates code. You already have a function that performs such allocation and which initialises its members and makes a head pointer point to it. That's really all you want to happen here!

So change the above code to:

newnode* head = NULL;
addnode(*head, 0);

You write in a code comment:

I assumed since push (i.e. addnode) takes a newnode** argument, that head should have an additional asterisk prefixed to it.

Yes, that is correct. This is needed because addnode will want to assign a new pointer value to "your" head, and so it needs to get the address of where to put it. It is a bit misleading to use the name head for this address in addnode, as it really is not the same thing as head in the main function. I suggest to rename it to headPtr. Also in C it is consider better practice to not cast the pointer returned by malloc

void push(newnode ** headPtr, int val) 
{
    newnode * new_node = malloc(sizeof(newnode));

    new_node->value = val;
    new_node->next = *headPtr;
    *headPtr = new_node;
}

I will now assume that version of the code with this corrected code for main:

    newnode* head = NULL;
    addnode(*head, 0);
    addnode(*head, 4);

...and make a visualisation of what happens when this gets executed. We start with head set to NULL:

┌─head─┐
│ NULL │
└──────┘

The addnode function is called with the address of head and the value 0. addnode has its own variable headPtr which now is a pointer to head:

┌─headPtr─┐
│    ┬    │
└────│────┘
     ▼
  ┌─head─┐
  │ NULL │
  └──────┘

With malloc a new node is allocated and initialised with value 0:

┌─headPtr─┐    ┌─new_node─┐
│    ┬    │    │     ┬    │
└────│────┘    └─────│────┘
     ▼               ▼
  ┌─head─┐      ┌──value──┐
  │ NULL │      │    0    │
  └──────┘      ├───next──┤
                │   ???   │
                └─────────┘

Then the assignment new_node->next = *headPtr; is made, which copies the value from *headPtr, i.e. from head, i.e. NULL... to that next element:

┌─headPtr─┐    ┌─new_node─┐
│    ┬    │    │     ┬    │
└────│────┘    └─────│────┘
     ▼               ▼
  ┌─head─┐      ┌──value──┐
  │ NULL │      │    0    │
  └──────┘      ├───next──┤
                │   NULL  │
                └─────────┘

Still, this new node is not yet "in the list": the final assignment of addnode will do this: *headPtr = new_node. This will copy the address of the node to *headPtr, i.e. to head:

┌─headPtr─┐    ┌─new_node─┐
│    ┬    │    │     ┬    │
└────│────┘    └─────│────┘
     ▼               ▼
  ┌─head─┐      ┌──value──┐
  │   ├────────►│    0    │
  └──────┘      ├───next──┤
                │   NULL  │
                └─────────┘

Note how that will influence what the main function will see when that first call to addnode has returned. head is now no longer NULL. Because we passed a pointer to head, addnode was able to modify head's (pointer) value. This is what we see in main:

  ┌─head─┐      ┌──value──┐
  │   ├────────►│    0    │
  └──────┘      ├───next──┤
                │   NULL  │
                └─────────┘

Let's do the second call of addnode in the same way. headPtr will again point to head, and this time the value is 4. A new node is allocated with malloc, and the value member is assigned 4 (I have to make a bit of room in the diagram for it):

┌─headPtr─┐    ┌─new_node─┐
│    ┬    │    │     ┬    │
└────│────┘    └─────│────┘
     │               ▼
     │         ┌──value──┐
     │         │    4    │ 
     │         ├───next──┤
     │         │   ???   │
     ▼         └─────────┘
  ┌─head─┐                       ┌──value──┐
  │   ├─────────────────────────►│    0    │
  └──────┘                       ├───next──┤
                                 │   NULL  │
                                 └─────────┘

Now we get to the "magic" two assignments. First new_node->next = *headPtr;. This will establish the link between the new node, and the already existing one (from the previous call to add_node):

┌─headPtr─┐    ┌─new_node─┐
│    ┬    │    │     ┬    │
└────│────┘    └─────│────┘
     │               ▼
     │         ┌──value──┐
     │         │    4    │ 
     │         ├───next──┤
     │         │    ├──────┐
     ▼         └─────────┘ │
  ┌─head─┐                 │     ┌──value──┐
  │   ├────────────────────┴────►│    0    │
  └──────┘                       ├───next──┤
                                 │   NULL  │
                                 └─────────┘

And finally, *headPtr = new_node; will make the new node the first node of the list:

┌─headPtr─┐    ┌─new_node─┐
│    ┬    │    │     ┬    │
└────│────┘    └─────│────┘
     │               ▼
     │         ┌──value──┐
     │         │    4    │ 
     │         ├───next──┤
     │     ┌──►│    ├──────┐
     ▼     │   └─────────┘ │
  ┌─head─┐ │               │     ┌──value──┐
  │   ├────┘               └────►│    0    │
  └──────┘                       ├───next──┤
                                 │   NULL  │
                                 └─────────┘

When back in main, this is what remains:

  ┌─head─┐     ┌──value──┐       ┌──value──┐
  │   ├───────►│    4    │       │    0    │
  └──────┘     ├───next──┤       ├───next──┤
               │    ├───────────►│   NULL  │
               └─────────┘       └─────────┘

Hope this clarifies it.

trincot
  • 317,000
  • 35
  • 244
  • 286