CodeBlanks Logo Icon CodeBlanks

Summary - Dynamic data

  • C course
  • Intermediate
  • Lesson 75/116
  • 5 min

Combine linked list, stack and queue to write an undo/redo system.

Summary - Dynamic data

In this group we built a linked list from scratch: node definition, prepending, traversal and printing, deletion, and an array-based stack. Now we bring all pieces together.

Mini task: build the list 10 -> 20 -> 30, print it, delete the middle value 20, print again and free all memory. This is the typical life cycle of dynamic data in a real C program: build, use, modify, clean up. Every node allocated with malloc must be released with free; forgetting causes a leak. The traversal loop advances with cur = cur->next; it stops when it sees NULL.

Practice Bring all pieces together

Write list_push_front, list_print and list_remove. In main prepend 30, 20, 10; call list_print(head);; do head = list_remove(head, 20);; call list_print(head); again; finally free all memory with while (head) { Node *t = head; head = head->next; free(t); }.

Starter code

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

// list_push_front: prepend
// list_print: traverse and print
// list_remove: delete value
// main: prepend 30, 20, 10; print; remove 20; print; free all

int main(void) {
    // write here
    return 0;
}

Sample solution

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

Node *list_push_front(Node *head, int v) {
    Node *n = malloc(sizeof(Node));
    n->data = v; n->next = head;
    return n;
}

void list_print(Node *head) {
    Node *cur = head;
    while (cur) { printf("%d ", cur->data); cur = cur->next; }
    printf("\n");
}

Node *list_remove(Node *head, int v) {
    if (!head) return NULL;
    if (head->data == v) { Node *t = head; head = head->next; free(t); return head; }
    Node *prev = head, *cur = head->next;
    while (cur) {
        if (cur->data == v) { prev->next = cur->next; free(cur); return head; }
        prev = cur; cur = cur->next;
    }
    return head;
}

int main(void) {
    Node *head = NULL;
    head = list_push_front(head, 30);
    head = list_push_front(head, 20);
    head = list_push_front(head, 10);
    list_print(head);
    head = list_remove(head, 20);
    list_print(head);
    while (head) { Node *t = head; head = head->next; free(t); }
    return 0;
}

Expected output

10 20 30 
10 30

Care with dynamic data

Dynamic data is powerful but demands care. Three core rules: every malloc needs a free, never use freed memory, don't forget the head may change when editing a list.

The delete function must always return the new head; the caller takes it back with head = list_remove(head, x);. This pattern protects against the head being deleted. During traversal, freeing a node and then reading cur->next is wrong; save next first, then free. Stacks and queues are simple array-based variants of these patterns; the same rules apply.

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 List life cycle

Term Meaning
build with push_front
print with list_print
delete with list_remove
clean up with free

2 What must be done to fully clean up a list?

  • set head = NULL
  • free only the first node
  • call list_print
  • free every node Correct answer

Explanation: Every malloc needs a matching free.

All lessons in Intermediate