Search code examples
clinked-listmallocnodesfree

Simple linked list program just isn't working


I'm just scratching the surface with nodes and linked lists, so I've been trying to create a linked lists that prints out nodes from 1-10. However, it's full of problems. The program gives me a runtime error and segmentation fault, I also have errors when running valgrind.

The comments are more for me, to show that I (hopefully) know what each command is doing

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

int main(void) {
    typedef struct node {
        int value;
        struct node* next;
    }
    node;

    //creates nodes for head, tmp, content
    node* head = NULL;
    node* tmp = NULL;
    node* content = NULL;

    head->next = content; //head node points to content

    for (int i = 1; i <= 10; i++) {
        content = malloc(sizeof(node)); //creates new node
        content->value = i; //node data becomes i
        tmp->next = content; //tmp node points to content node
        tmp = tmp->next; //tmp node becomes next content node
        content->next = NULL; //content node points to null
        printf("%i ", content->value); //see node value
    }

    while (head != NULL) {
        node* temp = head;
        head = head->next;
        free(temp);
    }
    return 0;
}

Solution

  • Ok, so let's break this down

    section by section

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
        // Data section
        typedef struct node {
            int value;
            struct node* next;
        }
        node;
        node* head = NULL; // a pointer to what will be the head of the list
        node* content; // a utility pointer
        node* temp; // a utility pointer
        int i; // iteration var
    
        // Code Section
        for (i = 1; i <= 10; i++) {
            if (head == NULL) {
                // The list is empty when this condition triigers
                // we initialize the first node and assign head to point to it
                head = (node*)malloc(sizeof(node)); // allocate memory to the pointer. Also make sure the memory pointed to is of a node type
                                                    // This is important because it will allow us use -> operator to assign values to the pointed
                                                    // at memory
                head -> value = i; // assign the value i to the value field of the pointed to memory
                head -> next = NULL; // assign the next pointer of the pointed to memory to NULL
            } else {
                // iterate over the list till we reach the end. 
                // Once we do, assign more memory at the end, assign the memory a value and make it's next pointer be NULL
                content = head;
                while (content -> next != NULL) {
                    content = content -> next;
                }
                content -> next = (node*)malloc(sizeof(node));
                content -> next -> value = i;
            }
    
        }
    
        while (head != NULL) {
            temp = head;
            printf("Node data: %d\n", temp -> value);
            head = head->next;
            free(temp);
        }
        return 0;
    }
    

    Data section

    // Data section
        typedef struct node {
            int value;
            struct node* next;
        }
        node;
        node* head = NULL; // a pointer to what will be the head of the list
        node* content; // a utility pointer
        node* temp; // a utility pointer
        int i; // iteration var
    

    When I learnt to program in C if you didn't declare the vars your function used upfront at the top of the function, the compiler would complain. For some reason it works now.

    When you declare something as a pointer, you are only declaring it. You cannot assign values to it before you allocate memory to the pointer.

    Code Section (I don't know what else to call it)

    // Code Section
        for (i = 1; i <= 10; i++) {
            if (head == NULL) {
                // The list is empty when this condition triigers
                // we initialize the first node and assign head to point to it
                head = (node*)malloc(sizeof(node)); // allocate memory to the pointer. Also make sure the memory pointed to is of a node type
                                                    // This is important because it will allow us use -> operator to assign values to the pointed
                                                    // at memory
                head -> value = i; // assign the value i to the value field of the pointed to memory
                head -> next = NULL; // assign the next pointer of the pointed to memory to NULL
            } else {
                // iterate over the list till we reach the end. 
                // Once we do, assign more memory at the end, assign the memory a value and make it's next pointer be NULL
                content = head;
                while (content -> next != NULL) {
                    content = content -> next;
                }
                content -> next = (node*)malloc(sizeof(node));
                content -> next -> value = i;
            }
    
        }
    

    The right way to allocate memory and make sure the allocate memory is of a specific type is using malloc and cast it to the appropriate type. As you can see in some lines like content -> next = (node*)malloc(sizeof(node));. Ensuring the type of the memory allocated is done with the typecast (node*)

    What you were doing wrong

    1. head->next = content; is wrong. head at the time of this statement in your code is NULL. It doesn't have a next pointer that you can point to anything.
    2. tmp->next = content; same as above
    3. content = malloc(sizeof(node)); works best with the typecast I outlined above
    4. As others have pointed out, malloc can fail for a variety of reasons. It returns a NULL which you should check for.
    5. You never actually make head the head of your list