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
#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
#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.