0

I trying to implement my own linked list and have been messing around with the code learning about dynamic memory allocation and pointers and such. When I try to add something to my linked list I get a segfault, and upon using the debugger I realized that it was because initially my linked list's head pointer was not pointing to null and then my add function was not recognizing the head as being empty. But I have an initialize function that is setting the linked list's head pointer to NULL but for some reason once I exit out of the initialize function and into the add function, the head is no longer pointing to NULL.

Here's my code:

list.h

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

typedef struct list{
    Node *head;
} List;

list.c

void initialize(List *l){
    if(l != NULL){
        l = malloc(sizeof(List));
        l->head = NULL;
    }
}

void add(List *l, int a){
    //Code
}

int main(){
    List l;
    initialize(&l)
    add(&l, 2);
}

As soon as I step into the add function and print out *l, I see that the head is not pointing to 0x0. And I've been scratching my head as to why it's not. I thought it was something to do with pass by value but I don't think it is. What am I doing wrong here?

3
  • suggest performing a search of stackoverflow.com. This question has been answered many times. Commented Apr 25, 2016 at 20:48
  • Since it was more of a conceptual problem about pointers specific to my case, I don't think a search of people asking homeowrk questions will help me here. Commented Apr 25, 2016 at 22:11
  • regardless of the misunderstanding (yours) about pointer usage, the root of the problem with the posted code is the same as many of the questions about linked lists on stackoverflow. Commented Apr 26, 2016 at 13:44

3 Answers 3

2

Yes, pass-by-value is your culprit. You are passing a pointer by value.

Suppose l in your main() is at address 0xABCD. Then your main() gets compiled to

int main(void) {
    List l;
    initialize(0xABCD);
    add(0xABCD, 2);
}

and your initialize() call looks like this (suppose malloc() succeeds and allocates memory at address 0xCDEF:

void initialize(List *l) {
    if(l != 0x0) {
        l = 0xCDEF;    // malloc()
        l->head = 0x0;
    }
}

That l = 0xCDEF does not propagate to main(), because l was passed by value.

What you want to do is

void initialize(List **l) {
    if(l != NULL) {
        *l = malloc(sizeof(List)); // note dereferencing the passed-by-value pointer
        (*l)->head = NULL;
    }
}

int main(void) {
    List * l;
    initialize(&l);
    add(l, 2);
}

which will pass pointer to pointer to list (actually the address of the pointer in your main(). It allows the code in initialize() to change the l variable in main().

Alternatively, you can use

List * list_init() {
    List * retval = malloc(sizeof(List));
    if(retval == NULL) { // you should check malloc return value
        // abort(), print warning or just
        return NULL;
    }
    retval->head = NULL;
    return retval;
}

int main(void) {
    List * l = list_init();
    if(l == NULL) {
        // handle the error
    }
    add(l, 2);
}
Sign up to request clarification or add additional context in comments.

7 Comments

Note that it is far more common for ADT initialization functions to have a List * initialize(); prototype and do an equivalent of return l; to return the malloc'ed pointer.
Ah yes. I see now. Is there no other way to do it except with a double pointer?
I'm not constricted by the prototype for initialize but let's say I was and the function only took a single pointer as an argument? I'm just trying to understand copying, dereferencing etc more.
Thank you! This is helping very much. And just as a final twist, what if the function had to be void? void initialize(List *l). Surely there's no way to do this right?
Yes. If the function prototype was void initialize(List *l), there is no way you can make it behave correctly, unless the l is already malloc'ed and you only set the head member.
|
1

You declare a List in main() that lives on the stack. You pass a pointer to that List to initailize(). You then create a new List on the heap. When you return from initialize() you still are using the List on the stack that you had in the beginning. The List on the heap is leaked and you cannot access it. So you never initialized the List you pass as a pointer to add(). You can forget about initialize() and just have

l.head = NULL;

instead.

Comments

0

Did you code compile this line l->malloc(sizeof(list)); seems odd.

Create a structure with only one argument is not really useful, a simple typedef should do the job : typedef Node* List

2 Comments

It isn't odd. That's how you allocate a node in a linked list.
The question has been edited the line l->malloc(sizeof(list)); will not compile, l = malloc(sizeof(List)); will

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.