Week 8 Lab Linked Lists
Lab: Linked Lists¶
COP 3223C — TA-Led Lab (50 minutes)¶
Prerequisites: Both linked list lectures.
Learning Objectives¶
By the end of this lab, students will be able to:
- Build and traverse a singly-linked list using
mallocand pointer manipulation - Implement insertion, deletion, and search on a linked list
- Correctly free an entire linked list without memory errors
- Recognize and explain common linked-list pointer bugs
Setup (5 minutes)¶
Compile with address sanitizer throughout:
All exercises use this node definition — put it in node.h once and include it everywhere:
/* node.h */
#ifndef NODE_H
#define NODE_H
typedef struct Node {
int data;
struct Node *next;
} Node;
/* Allocate a node; returns NULL
on failure */
Node *node_create(int data);
/* Free a node (not the list) */
void node_free(Node *n);
#endif
/* node.c */
#include "node.h"
#include <stdlib.h>
Node *node_create(int data) {
Node *n = malloc(sizeof(Node));
if (!n) return NULL;
n->data = data;
n->next = NULL;
return n;
}
void node_free(Node *n) {
free(n);
}
Part 1: Build and Print (10 minutes)¶
Task¶
Write list1.c. Complete the three functions below, then test with main.
/* list1.c */
#include <stdio.h>
#include <stdlib.h>
**include**{: #include .hash} "node.h"
/* Insert at front — return
new head */
Node *prepend(
Node *head, int data)
{
/* TODO */
return head;
}
/* Insert at back — return head */
Node *append(
Node *head, int data)
{
/* TODO */
return head;
}
/* Print: 1 -> 2 -> 3 -> NULL */
void list_print(Node *head) {
/* TODO */
}
/* Free entire list */
void list_free(Node *head) {
/* TODO */
}
int main(void) {
Node *head = NULL;
/* Build: 10 -> 20 -> 30 */
head = append(head, 10);
head = append(head, 20);
head = append(head, 30);
list_print(head);
/* Prepend 5 */
head = prepend(head, 5);
list_print(head);
list_free(head);
head = NULL;
return 0;
}
Expected Output¶
Part 2: Search and Delete (12 minutes)¶
Task¶
Extend your work into list2.c. Add the three functions below. Copy your working prepend, append, list_print, and list_free from Part 1.
/* Return pointer to first node
with data == target, or NULL */
Node *list_search(
Node *head, int target)
{
/* TODO */
return NULL;
}
/* Remove first node where
data == target.
Return (possibly new) head. */
Node *list_delete(
Node *head, int target)
{
/* TODO */
return head;
}
/* Return number of nodes */
int list_length(Node *head) {
/* TODO */
return 0;
}
Test main¶
int main(void) {
Node *head = NULL;
int i;
/* Build 1 -> 2 -> 3 -> 4 -> 5 */
for (i = 1; i <= 5; i++)
head = append(head, i);
list_print(head);
/* Search */
Node *found =
list_search(head, 3);
if (found)
printf("Found: %d\n",
found->data);
else
printf("Not found\n");
/* Delete middle */
head = list_delete(head, 3);
list_print(head);
/* Delete head */
head = list_delete(head, 1);
list_print(head);
/* Delete tail */
head = list_delete(head, 5);
list_print(head);
/* Delete non-existent */
head = list_delete(head, 99);
list_print(head);
printf("Length: %d\n",
list_length(head));
list_free(head);
return 0;
}
Expected Output¶
1 -> 2 -> 3 -> 4 -> 5 -> NULL
Found: 3
1 -> 2 -> 4 -> 5 -> NULL
2 -> 4 -> 5 -> NULL
2 -> 4 -> NULL
2 -> 4 -> NULL
Length: 2
Part 3: Sorted Insertion (13 minutes)¶
Task¶
Write list3.c. Build a sorted singly-linked list from user input — all integers should be kept in ascending order at all times. No sorting after the fact: insert directly into the correct position.
/* Insert data so the list
stays sorted ascending.
Return (possibly new) head. */
Node *insert_sorted(
Node *head, int data)
{
/* TODO */
return head;
}
Test main¶
int main(void) {
Node *head = NULL;
int vals[] = {
5, 2, 8, 1, 9, 3, 7
};
int n = 7, i;
for (i = 0; i < n; i++) {
head = insert_sorted(
head, vals[i]
);
list_print(head);
}
list_free(head);
return 0;
}
Expected Output¶
5 -> NULL
2 -> 5 -> NULL
2 -> 5 -> 8 -> NULL
1 -> 2 -> 5 -> 8 -> NULL
1 -> 2 -> 5 -> 8 -> 9 -> NULL
1 -> 2 -> 3 -> 5 -> 8 -> 9 -> NULL
1 -> 2 -> 3 -> 5 -> 7 -> 8 -> 9 -> NULL
Part 4: Bug Hunt (10 minutes)¶
Each snippet has one or more bugs. Identify what goes wrong and how to fix it.
Snippet A — Traversal that destroys the list:
Node *head = /* some list */;
while (head != NULL) {
printf("%d\n", head->data);
head = head->next;
}
/* What is head now? */
Snippet B — Use-after-free:
Snippet C — Unlinked insert:
Snippet D — Leaking delete:
Node *list_delete(
Node *head, int val)
{
Node *cur = head;
while (cur->next) {
if (cur->next->data
== val) {
cur->next =
cur->next->next;
return head;
}
cur = cur->next;
}
return head;
}
Snippet E — NULL dereference on empty list:
Node *list_delete(
Node *head, int val)
{
if (head->data == val) {
Node *tmp = head->next;
free(head);
return tmp;
}
/* ... */
}
Answers (TA — reveal after discussion)¶
- A:
headis advanced to NULL, destroying the only reference to the list. Use a separatecurpointer. - B:
cur->nextis read afterfree(cur)— undefined behavior. Savenext = cur->nextbeforefree(cur), thencur = next. - C:
new_node->nextis set, but nothing points tonew_node— it is immediately leaked. Should behead->next = new_nodeor reassign the predecessor’snext. - D: The deleted node
cur->nextis neverfree()‘d — memory leak. - E: If
head == NULL,head->datais a NULL pointer dereference — segfault. Must guard withif (!head) return NULL;first.
Wrap-Up & Discussion (5 minutes)¶
TA-led questions (pick 2–3):
- In
list_delete, why do we need to handle the head case separately from the middle/tail case? - What would happen if we called
list_freeon a circular linked list with the implementation from Part 1? - In Part 3, what is the time complexity of building the entire sorted list by repeated
insert_sorted? (Hint: think about what each insertion costs.) - Could you implement
insert_sortedwithout a special case for inserting before the head? Describe how. - If you needed O(1) append AND O(1) prepend, what single additional pointer would you add to your list? Where would you maintain it?
Submission¶
No submission required. TAs should check off completion of Parts 1–3.
TA Reference Solutions¶
Part 1¶
Node *prepend(
Node *head, int data)
{
Node *n = node_create(data);
if (!n) return head;
n->next = head;
return n;
}
Node *append(
Node *head, int data)
{
Node *n = node_create(data);
if (!n) return head;
if (!head) return n;
Node *cur = head;
while (cur->next)
cur = cur->next;
cur->next = n;
return head;
}
void list_print(Node *head) {
Node *cur = head;
while (cur) {
printf("%d", cur->data);
if (cur->next)
printf(" -> ");
cur = cur->next;
}
printf(" -> NULL\n");
}
void list_free(Node *head) {
Node *cur = head, *nxt;
while (cur) {
nxt = cur->next;
free(cur);
cur = nxt;
}
}
Part 2¶
Node *list_search(
Node *head, int target)
{
Node *cur = head;
while (cur) {
if (cur->data == target)
return cur;
cur = cur->next;
}
return NULL;
}
Node *list_delete(
Node *head, int target)
{
if (!head) return NULL;
if (head->data == target) {
Node *tmp = head->next;
free(head);
return tmp;
}
Node *cur = head;
while (cur->next) {
if (cur->next->data
== target) {
Node *del = cur->next;
cur->next = del->next;
free(del);
return head;
}
cur = cur->next;
}
return head;
}
int list_length(Node *head) {
int n = 0;
Node *cur = head;
while (cur) { n++; cur=cur->next; }
return n;
}
Part 3¶
Node *insert_sorted(
Node *head, int data)
{
Node *n = node_create(data);
if (!n) return head;
if (!head ||
data <= head->data) {
n->next = head;
return n;
}
Node *cur = head;
while (cur->next &&
cur->next->data < data)
cur = cur->next;
n->next = cur->next;
cur->next = n;
return head;
}
Created : July 24, 2026