Laboratory: Linked Lists

Gábor Horváth / Zsolt Kohári · 2020.11.26.

Handling of singly linked simple lists

The problems below should be solved using singly linked simple lists (without sentinel). All basic list operations must be realized: traversing, insertion and deleting from the list. It is advantageous to draw the pointers and the interesting elements and how the given operation works: how the pointer values change.

Preparation for lab:

  • Review lecture topics on pointers
  • Review lecture topics on linked lists

1. Memory handling error

There is a mistake in the code similar to those seen on the previous practice. Do you get any compiler message? What happens when you run the program and why? Is there any difference between the two versions? Discuss it with your lab conductor.

#include <stdio.h>

int *f1(int i) {
    return &i;
}

int main(void) {
    int *p;
    p = f1(10);
    printf("%d\n", *p);
    printf("%d\n", *p);
    return 0;
}
#include <stdio.h>

int *f1(int i) {
    return &i;
}

int main(void) {
    int x = 10;
    int *p = f1(x);
    printf("%d\n", *p);
    printf("%d\n", *p);
    return 0;
}

Solution

Returning the address of a local variable from a function is a big mistake. The parameter behaves like a local variable; it is destroyed upon exiting the function. Returning its address to the caller causes undefined behaviour when derefencing it.

2. Print the list

See the code below. The program builds a singly linked list from the content of the array, head points to the beginning of the list.

#include <stdlib.h>

typedef struct ListEl {
    int data;
    struct ListEl *nxt;
} ListEl;


/* creates a new list from the array elements */
ListEl *list_build(void) {
    int values[] = { 8, 14, 13, 17, 1, 19, 16, 5, 3, 11, 2,
                     15, 9, 10, 6, 22, 4, 7, 18, 27, -15 };
    ListEl *lis = NULL;

    int i;
    for (i = 0; values[i] > -1; ++i) {
        ListEl *u;
        u = (ListEl*) malloc(sizeof(ListEl));
        u->nxt = lis;
        u->data = values[i];
        lis = u;
    }
    return lis;
}


int main(void) {
    ListEl *head;
    
    head = list_build();
    
    ....
    
    return 0;   
}

Copy and paste the code into a new project! Write a loop that prints the values stored in the list! You should get this output:

27 18 7 4 22 6 10 9 15 2 11 3 5 16 19 1 17 13 14 8

Why are the values reversed?

3. Length of the list

Insert a code segment that counts the length of the list! To check it compare the calculated number to the number of elements printed.

After testing, move and turn this code segment into a function that receives the list as parameter and returns its length!

4. Delete the list

There is memory leak in the program above. Write a loop that deletes the elements of the list! (Do NOT use recursion!) Move this loop into a function that receives the list as parameter.

5. Insert at head

Write a function that receives a list and an integer value as parameters. Insert the value as the first element of the list (at the head).

This way the head pointer in main must change for each insertion. Thus the function must either return the new head pointer, or take the head pointer parameter by address. Choose the first now:

head = list_insert_athead(head, 25);

Insert a few values and print the list. The inserted values print in reverse order, of course. If your function works fine, delete the function list_build() provided in the code above, and replace it with this new function.

6. Insert at the end

Write a function that receives a list and an integer value as parameters. Insert the value as the last element of the list. Pay special attention to make it work for an empy list as well (NULL pointer parameter)! In this case the head pointer changes, so it must be handled like in the previous function.

7. Search a value

Create a function that takes a list and an integer value parameter. Return a pointer to the first such element of the list that contains the received integer. Return NULL if there is no such element.

Solution for all

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


/* an element of the list: */
typedef struct ListEl {
    int data;              /* The data to store */
    struct ListEl *nxt;    /* Pointer to the next list element */
} ListEl;


/* Printing the list */
void list_print(ListEl *head) {
    ListEl *p;
    for (p = head; p != NULL; p = p->nxt)
        printf("%d ", p->data);
    printf("\n");
}

/* Releasing a list */
void list_free(ListEl *head) {
    ListEl *p = head;
    /* Traversing the list */
    while (p != NULL) {
        /* Just writing free(p); is wrong, the rest of the list would be lost */
        /* hence p=p->nxt; would be invalid */
        ListEl *tmp = p->nxt; /* Store the pointer to the rest of the list */
        free(p); /* Now we can delete the current element */
        p = tmp; /* Keep releasing the list from the next element on */
    }
}

/* Inserting a new element to the head of the list */
ListEl *list_insert_athead(ListEl *head, int what) {
    ListEl *newel = (ListEl *) malloc(sizeof(ListEl)); /* New element */
    newel->data = what; /* Copy data to store to the list element */
    newel->nxt  = head; /* After the new element goes the rest of the list (even if it is empty) */
    return newel; /* The new element will be the new head of the list */
}

/* Inserting a new element to the end of the list */
ListEl *list_insert_atend(ListEl *head, int what) {
    ListEl *newel = (ListEl *) malloc(sizeof(ListEl)); /* New element */

    newel->data = what; /* Copy data to store to the list element */
    newel->nxt  = NULL; /* This will be the last element of the list, next pointer is NULL */
    if (head == NULL) {
        return newel; /* If the list was empty, the new element is the head of the list */
    } else {
        ListEl *p = head; /* Walk to the end of the list */
        while (p->nxt != NULL) p = p->nxt;
        p->nxt = newel; /* ... and add the new element to the chain */
        return head; /* The head of the list did not change in this case */
    }
}

/* Return length of the list */
int list_length(ListEl *head) {
    /* Traversing the list and counting the number of elements */
    int len = 0;
    ListEl *p = head;
    while (p != NULL) {
        len++;
        p = p->nxt;
    }
    return len;
}

/* Searching for an element in the list */
ListEl *list_search(ListEl *head, int what) {
    ListEl *p;
    for (p = head; p != NULL; p = p->nxt)
        if (p->data == what)
            return p;
    return NULL;
}

/* The main program */
int main(void) {
    ListEl *head = NULL;

    head = list_insert_atend(head, 66);
    head = list_insert_athead(head, 55);
    head = list_insert_atend(head, 77);
    head = list_insert_athead(head, 44);
    head = list_insert_athead(head, 33);
    head = list_insert_athead(head, 22);
    head = list_insert_athead(head, 11);
    list_print(head);
    printf("\nLength: %d\n", list_length(head));

    ListEl *hit = list_search(head, 44);
    if (hit != NULL)
        printf("%d\n", hit->data);
    else
        printf("no such data in the list\n");

    list_free(head);

    return 0;
}

8. Further problems for practicing

Copying a list

It was a ST

Define a type for a simple singly linked list, the data member of which is an array of 20 int values! Write a function that receives a list as parameter. It should create an identical copy of the received list and return it. (So it should create another list with the same elements, in the same order.)

Delete the fifth element

It was a ST

Write a function that deletes the fifth element of a list! If there are less than 5 elements, nothing to be done. The function has a single parameter: the head pointer.

Define a type for a simple singly linked list, the data member of which is a word of max. 30 characters. Make a drawing and number the required steps. Demonstrate the usage of the function: define a list, suppose it has some values in it, then delete the fifth element using the function.

Insert into fifth position

It was a ST

Write a function that inserts a new value into a list in such a way that the new element will be the fifth one of the list! If there are less than 4 elements, insert at the end. The function has two parameters: the head pointer and the data to be inserted.

Define a type for a simple singly linked list, the data member of which is a name of max. 50 characters. Make a drawing and number the required steps. Demonstrate the usage of the function: define a list, suppose it has some values (at least one value) in it, then insert Donald Duck into fifth position using the function.

From head to end

It was a ST

Write a function that moves the very first element of a list to the very last position! (Like from a list of 1,2,3,4,5 the modified list should be 2,3,4,5,1.) If there are less than two elements, nothing to be done.

Define a type for a simple singly linked list, the data member of which is an integer value. Make a drawing and number the required steps. Demonstrate the usage of the function: create a list of two elements, then move the first one to the end using the function.

From end to head

It was a ST

Write a function that moves the very last element of a list to the very first position! (Like from a list of 1,2,3,4,5 the modified list should be 5,1,2,3,4.) If there are less than two elements, nothing to be done.

Define a type for a simple singly linked list, the data member of which is a real value. Make a drawing and number the required steps. Demonstrate the usage of the function: create a list of two elements, then move the last one to the head using the function.