1

I have understood how to add a new element using double pointer through the following code

void insertFirst(struct DLinkedList* *first, int el)
{
struct DLinkedList *newFirst;

    newFirst = (struct DLinkedList*)malloc(sizeof(struct DLinkedList));

    if (newFirst == NULL)
    {
        printf("ERROR!");
    }

    else
    {
        newFirst->sensorData = el;
        newFirst->next = *first;

        (*first) = newFirst;

        //printf("First node has been updated!");
    }
}

However what if I need to use a pointer to the newly created element instead of int

void insertFirst(struct DLinkedList* *first, struct DLinkedList* el)

It not only gives me error in the function but also when I call the function in main() I cannot write insertFirst(&first, 10); since 10 is not a pointer.

Maso
  • 35
  • 5

2 Answers2

1

You need to allocate memory for the el structure (in the main function), just like you have done for newFirst structure, assign the element 10 to it (e.g., el->sensorData = 10). Pass the el structure to the function insertFirst(&first, el), and adapt the function insertFirst, namely you do not need the newFirst struct inside that function any longer, since you will receive it as a parameter (i.e., el) of the function. Consequently, you should be using the el structure instead of the newFirst.

Btw:

Regarding newFirst = (struct DLinkedList*)malloc(sizeof(struct DLinkedList));, in C you should not cast the malloc result, check this SO thread for more detail.

Here is a code example of what I mean:

#include <stdio.h>
#include <stdlib.h>

struct DLinkedList{
    int sensorData;
    struct DLinkedList *next;
};

int insertFirst(struct DLinkedList **first, struct DLinkedList *el)
{
    if (el == NULL){
        return 1; // means error
    }
    else{
        el->next = *first;
        (*first) = el;
        return 0; // means ok
    }
}

void print_list(struct DLinkedList* l){
    for(; l != NULL; l = l->next){
        printf("%d\n", l->sensorData);
    }
}


int main() {
    struct DLinkedList *head = NULL;
    struct DLinkedList *el = malloc(sizeof(struct DLinkedList));
    el->sensorData = 10;
    el->next = NULL;
    
    if(insertFirst(&head, el) != 0)
        printf("ERROR!");
    
    print_list(NULL);
    
    return 0;
}

You can use void as the return insertFirst(struct DLinkedList **first, struct DLinkedList *el), I am justing using int, so that I can move the error handling logic (i.e., printf("ERROR!");) from inside the insertFirst function, to the main function instead.

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
0

Let's think about what you need to do. You have a pointer to a new node el and you want to make that node the new first node in your list. You have the address of the first node. To make el your new first node, you need to have el->next point to the first node, and then replace the node at the address of the first node with el. This method is called forward-chaining. (ironically, forward-chaining results in a list that is in the reverse order from how the nodes were added)

A trivial implementation would be:

void insertFirst(struct DLinkedList **first, struct DLinkedList *el)
{
  if (*first != NULL) {       /* if list not-empty */
    el->next = *first;        /* set el->next to head node */
  
  *first = el;                /* replace node at address of *first with el */
}

But, what happens if the list is currently empty? If you are adding el as the first node, you must ensure the el->next pointer is initialized NULL. (which is may be in the function that creates the node, but that isn't shown). To handle that case to ensure the list is valid, you would add:

void insertFirst(struct DLinkedList **first, struct DLinkedList *el)
{
  if (*first != NULL)         /* if list not-empty */
    el->next = *first;        /* set el->next to head node */
  else                        /* list empty */
    el->next = NULL;          /* set el->next to NULL */
  
  *first = el;                /* replace node at address of *first with el */
}

At this point we have validated whether the list is empty or not, and we have ensured that the el->next pointer is NULL if el is added as the first node in an empty-list, but what if el itself is NULL when your function is called?

You can protect against that case, fully validating all parameters in your function as follows:

void insertFirst(struct DLinkedList **first, struct DLinkedList *el)
{
  if (el == NULL)             /* validate el not NULL, if so return */
    return;
  
  if (*first != NULL)         /* if list not-empty */
    el->next = *first;        /* set el->next to head node */
  else                        /* list empty */
    el->next = NULL;          /* set el->next to NULL */
  
  *first = el;                /* replace node at address of *first with el */
}

The level of validation needed above will depend on the remainder of your code not shown in your question. When in doubt, the last version will cover all bases. The only case not covered above is the case where *first is uninitialized and *el == NULL -- that case is left to you as an exercise. (but handling the case where*first is indeterminate isn't something that can be done in your insertFirst() function)

Now, after going though each of the considerations surround whether the list is empty or you are adding to an existing list, and in checking whether the el parameter is not NULL, your insertFirst() function boils down to the following -- where the forward-chaining operation is the same, regardless of the state of the list when insertFirst() is called:

void insertFirst(struct DLinkedList **first, struct DLinkedList *el)
{
  if (el == NULL)             /* validate el not NULL, if so return */
    return;
  
  el->next = *first;          /* set el->next to head node */
  *first = el;                /* replace node at address of *first with el */
}

(so long as your list is initialized properly)

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Sorry, maybe I am missing something obvious. But if *first == NULL, then el->next = *first;, and consequently el->next == NULL, so what do you need the else part? – dreamcrash Nov 15 '20 at 09:25
  • If `*first == NULL`, you know that the list is empty and nothing else. You haven't validated that `el->next` was initialized properly when the node was created (no way to do that -- the code isn't shown). So the best you can do is if `*first == NULL` is to set `el->next == NULL;` so after you set `*first = el;` you have a valid list where `head->next == NULL` back in the caller. If `el->next` wasn't initialized and you just set `*first = el;` then any attempt to iterate over the list would result in a SegFault. (e.g. your list would be `head->next = indeterminate address;`) – David C. Rankin Nov 15 '20 at 09:30
  • Thanks for the explanation, but, than you only need this lines of code if (*el == NULL) return; el->next = *first; *first = el; They are semantically the same as you have posted but with less code lines, albeit less straight forward – dreamcrash Nov 15 '20 at 09:33
  • btw instead of if (*el == NULL) you should have if (el == NULL) – dreamcrash Nov 15 '20 at 09:35
  • Yes, you are correct, you can lay out the conditional checks any way you like. The purpose in taking them sequentially was to show the considerations. – David C. Rankin Nov 15 '20 at 09:35