
Application of the Iterator Pattern in C Language (Including Linux Kernel Instances)
1. Definition and Core Value of the Iterator Pattern
The Iterator Pattern is a behavioral design pattern whose core is to provide a unified way to traverse elements in aggregate objects (such as arrays, linked lists, trees, etc.) without exposing the internal structure of the aggregate object. The iterator encapsulates the traversal logic, decoupling the aggregate object from the traversal behavior while supporting multiple traversal methods (such as forward, backward, and jump traversal).
Significance: The internal structure differences of different aggregate objects are significant (for example, arrays are contiguous in memory, while linked lists consist of discrete nodes). Implementing traversal logic directly in the aggregate object can lead to code coupling and make it difficult to switch traversal methods flexibly. The iterator pattern abstracts the traversal interface, allowing the client to traverse different aggregate objects with the same code, enhancing code reusability and flexibility.
Problems Solved:
- • The client needs to understand the internal structure of the aggregate object to traverse it (for example, a linked list requires manipulating the
<span>next</span>pointer, while an array requires manipulating indices); - • The same aggregate object requires multiple traversal methods (such as forward and backward traversal);
- • Traversal logic is strongly coupled with the aggregate object, requiring modifications to the aggregate object code to change the traversal method.
2. Core Idea of Implementing the Iterator Pattern in C Language
C language implements the iterator pattern through structs to encapsulate iterator state + function pointers to define traversal operations:
- 1. Define the iterator interface (Iterator): The struct includes function pointers for
<span>has_next</span>(to check if there is a next element),<span>next</span>(to get the next element),<span>reset</span>(to reset the iterator), and members to store the iteration state (such as current index, current node pointer); - 2. Implement the aggregate object (Aggregate): Define the interface for creating the iterator (
<span>create_iterator</span>), and the aggregate object itself maintains data storage (such as arrays, linked lists); - 3. Bind the iterator to the aggregate object: Each aggregate object creates a corresponding iterator instance, and the iterator internally holds a pointer to the aggregate object, implementing specific traversal logic through function pointers;
- 4. Client usage: Traverse the aggregate object through the iterator interface without needing to focus on the internal structure of the aggregate object.

3. 5 Examples
Example 1: Array Iterator (Basic Linear Traversal)
Implement a forward iterator for an array, supporting traversal of an integer array while hiding the details of index operations.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Iterator interface
typedef struct {
bool (*has_next)(struct Iterator* self);
int (*next)(struct Iterator* self);
void (*reset)(struct Iterator* self);
// Iterator state
int* data; // Pointer to array data
int size; // Array size
int current; // Current index
} Iterator;
// Aggregate object: Integer array
typedef struct {
int* data;
int size;
Iterator* (*create_iterator)(struct Array* self); // Create iterator
} Array;
// Array iterator's has_next implementation
static bool array_has_next(Iterator* self) {
return self->current < self->size;
}
// Array iterator's next implementation
static int array_next(Iterator* self) {
return self->data[self->current++];
}
// Array iterator's reset implementation
static void array_reset(Iterator* self) {
self->current = 0;
}
// Create array iterator
static Iterator* array_create_iterator(Array* self) {
Iterator* iter = malloc(sizeof(Iterator));
iter->has_next = array_has_next;
iter->next = array_next;
iter->reset = array_reset;
iter->data = self->data;
iter->size = self->size;
iter->current = 0;
return iter;
}
// Create array aggregate object
Array* create_array(int* data, int size) {
Array* arr = malloc(sizeof(Array));
arr->data = malloc(sizeof(int) * size);
memcpy(arr->data, data, sizeof(int) * size);
arr->size = size;
arr->create_iterator = array_create_iterator;
return arr;
}
// Free resources
void free_array(Array* arr) {
free(arr->data);
free(arr);
}
void free_iterator(Iterator* iter) {
free(iter);
}
// Client usage: Unified traversal interface
void traverse(Iterator* iter) {
printf("Traversing: ");
while (iter->has_next(iter)) {
printf("%d ", iter->next(iter));
}
printf("\n");
}
int main() {
int data[] = {1, 2, 3, 4, 5};
Array* arr = create_array(data, 5);
Iterator* iter = arr->create_iterator(arr);
traverse(iter); // Output: 1 2 3 4 5
iter->reset(iter); // Reset iterator
printf("First element after reset: %d\n", iter->next(iter)); // Output: 1
free_iterator(iter);
free_array(arr);
return 0;
}
The output of the above code is as follows
Traversing: 1 2 3 4 5
First element after reset: 1
The corresponding UML diagram is as follows

That is, the array iterator encapsulates the index operation, allowing the client to traverse the array through the <span>has_next</span> and <span>next</span> interfaces without directly manipulating indices, achieving decoupling of traversal logic from the array structure.
Example 2: Linked List Iterator (Discrete Node Traversal)
Implement an iterator for a singly linked list, supporting traversal of linked list nodes while hiding the <span>next</span> pointer operations of the linked list.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Linked list node
typedef struct Node {
int value;
struct Node* next;
} Node;
// Iterator interface
typedef struct {
bool (*has_next)(struct ListIterator* self);
int (*next)(struct ListIterator* self);
void (*reset)(struct ListIterator* self);
// Iterator state
Node* head; // Head node of the linked list
Node* current; // Current node
} ListIterator;
// Aggregate object: Singly linked list
typedef struct {
Node* head;
Node* tail;
ListIterator* (*create_iterator)(struct LinkedList* self);
} LinkedList;
// Linked list iterator's has_next implementation
static bool list_has_next(ListIterator* self) {
return self->current != NULL;
}
// Linked list iterator's next implementation
static int list_next(ListIterator* self) {
int value = self->current->value;
self->current = self->current->next;
return value;
}
// Linked list iterator's reset implementation
static void list_reset(ListIterator* self) {
self->current = self->head;
}
// Create linked list iterator
static ListIterator* list_create_iterator(LinkedList* self) {
ListIterator* iter = malloc(sizeof(ListIterator));
iter->has_next = list_has_next;
iter->next = list_next;
iter->reset = list_reset;
iter->head = self->head;
iter->current = self->head;
return iter;
}
// Initialize linked list
LinkedList* create_linked_list() {
LinkedList* list = malloc(sizeof(LinkedList));
list->head = NULL;
list->tail = NULL;
list->create_iterator = list_create_iterator;
return list;
}
// Add node to linked list
void list_add(LinkedList* list, int value) {
Node* node = malloc(sizeof(Node));
node->value = value;
node->next = NULL;
if (!list->head) {
list->head = node;
list->tail = node;
} else {
list->tail->next = node;
list->tail = node;
}
}
// Free linked list
void free_linked_list(LinkedList* list) {
Node* curr = list->head;
while (curr) {
Node* next = curr->next;
free(curr);
curr = next;
}
free(list);
}
// Free iterator
void free_list_iterator(ListIterator* iter) {
free(iter);
}
// Client usage: Unified traversal interface (same as array iterator usage)
void traverse(ListIterator* iter) {
printf("Traversing list: ");
while (iter->has_next(iter)) {
printf("%d ", iter->next(iter));
}
printf("\n");
}
int main() {
LinkedList* list = create_linked_list();
list_add(list, 10);
list_add(list, 20);
list_add(list, 30);
ListIterator* iter = list->create_iterator(list);
traverse(iter); // Output: 10 20 30
iter->reset(iter);
printf("First element after reset: %d\n", iter->next(iter)); // Output: 10
free_list_iterator(iter);
free_linked_list(list);
return 0;
}
The output of the above code is as follows
Traversing list: 10 20 30
First element after reset: 10
The corresponding UML diagram is as follows

That is, the linked list iterator encapsulates the <span>next</span> pointer operation, allowing the client to traverse using the same <span>has_next</span> and <span>next</span> interfaces as the array iterator, reflecting the core value of the iterator pattern of “unified traversal interface”.
Example 3: Backward Iterator (Supports Bidirectional Traversal)
Implement both forward and backward iterators for an array, allowing the client to choose the traversal direction based on needs.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Iterator interface (supports forward/backward)
typedef struct {
bool (*has_next)(struct DirectionalIterator* self);
int (*next)(struct DirectionalIterator* self);
void (*reset)(struct DirectionalIterator* self);
// Iterator state
int* data;
int size;
int current;
bool is_forward; // Forward/backward flag
} DirectionalIterator;
// Aggregate object: Array supporting bidirectional iteration
typedef struct {
int* data;
int size;
DirectionalIterator* (*create_forward_iterator)(struct BidirectionalArray* self);
DirectionalIterator* (*create_backward_iterator)(struct BidirectionalArray* self);
} BidirectionalArray;
// Forward iterator's has_next
static bool forward_has_next(DirectionalIterator* self) {
return self->current < self->size;
}
// Forward iterator's next
static int forward_next(DirectionalIterator* self) {
return self->data[self->current++];
}
// Forward iterator's reset
static void forward_reset(DirectionalIterator* self) {
self->current = 0;
}
// Backward iterator's has_next
static bool backward_has_next(DirectionalIterator* self) {
return self->current >= 0;
}
// Backward iterator's next
static int backward_next(DirectionalIterator* self) {
return self->data[self->current--];
}
// Backward iterator's reset
static void backward_reset(DirectionalIterator* self) {
self->current = self->size - 1;
}
// Create forward iterator
static DirectionalIterator* create_forward(BidirectionalArray* self) {
DirectionalIterator* iter = malloc(sizeof(DirectionalIterator));
iter->has_next = forward_has_next;
iter->next = forward_next;
iter->reset = forward_reset;
iter->data = self->data;
iter->size = self->size;
iter->current = 0;
iter->is_forward = true;
return iter;
}
// Create backward iterator
static DirectionalIterator* create_backward(BidirectionalArray* self) {
DirectionalIterator* iter = malloc(sizeof(DirectionalIterator));
iter->has_next = backward_has_next;
iter->next = backward_next;
iter->reset = backward_reset;
iter->data = self->data;
iter->size = self->size;
iter->current = self->size - 1;
iter->is_forward = false;
return iter;
}
// Create bidirectional array
BidirectionalArray* create_bidirectional_array(int* data, int size) {
BidirectionalArray* arr = malloc(sizeof(BidirectionalArray));
arr->data = malloc(sizeof(int) * size);
memcpy(arr->data, data, sizeof(int) * size);
arr->size = size;
arr->create_forward_iterator = create_forward;
arr->create_backward_iterator = create_backward;
return arr;
}
// Free resources
void free_bidirectional_array(BidirectionalArray* arr) {
free(arr->data);
free(arr);
}
void free_directional_iterator(DirectionalIterator* iter) {
free(iter);
}
// Client usage: Switch traversal direction
void traverse(DirectionalIterator* iter, const char* direction) {
printf("Traversing %s: ", direction);
while (iter->has_next(iter)) {
printf("%d ", iter->next(iter));
}
printf("\n");
}
int main() {
int data[] = {1, 2, 3, 4, 5};
BidirectionalArray* arr = create_bidirectional_array(data, 5);
DirectionalIterator* forward_iter = arr->create_forward_iterator(arr);
traverse(forward_iter, "forward"); // Output: 1 2 3 4 5
DirectionalIterator* backward_iter = arr->create_backward_iterator(arr);
traverse(backward_iter, "backward"); // Output: 5 4 3 2 1
free_directional_iterator(forward_iter);
free_directional_iterator(backward_iter);
free_bidirectional_array(arr);
return 0;
}
The output of the above code is as follows
Traversing forward: 1 2 3 4 5
Traversing backward: 5 4 3 2 1
The corresponding UML diagram is as follows

That is, the same iterator interface is used to implement both forward and backward traversal, allowing the client to switch directions without modifying the traversal logic, reflecting the iterator pattern’s flexible support for traversal methods.
Example 4: Binary Tree Iterator (Level Order Traversal)
Implement a level order traversal iterator for a binary tree, using a queue to cache nodes and hiding the details of tree-level traversal.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Binary tree node
typedef struct TreeNode {
int value;
struct TreeNode* left;
struct TreeNode* right;
} TreeNode;
// Queue: Auxiliary for level order traversal
typedef struct QueueNode {
TreeNode* tree_node;
struct QueueNode* next;
} QueueNode;
typedef struct {
QueueNode* front;
QueueNode* rear;
} Queue;
// Queue operations
Queue* create_queue() {
Queue* q = malloc(sizeof(Queue));
q->front = q->rear = NULL;
return q;
}
void enqueue(Queue* q, TreeNode* node) {
QueueNode* qn = malloc(sizeof(QueueNode));
qn->tree_node = node;
qn->next = NULL;
if (!q->rear) {
q->front = q->rear = qn;
} else {
q->rear->next = qn;
q->rear = qn;
}
}
TreeNode* dequeue(Queue* q) {
if (!q->front) return NULL;
QueueNode* qn = q->front;
TreeNode* tn = qn->tree_node;
q->front = q->front->next;
if (!q->front) q->rear = NULL;
free(qn);
return tn;
}
bool is_queue_empty(Queue* q) {
return q->front == NULL;
}
void free_queue(Queue* q) {
while (q->front) {
QueueNode* next = q->front->next;
free(q->front);
q->front = next;
}
free(q);
}
// Tree iterator
typedef struct {
bool (*has_next)(struct TreeIterator* self);
int (*next)(struct TreeIterator* self);
void (*reset)(struct TreeIterator* self);
// Iterator state
TreeNode* root; // Root node of the tree
Queue* queue; // Cache for nodes to be traversed
} TreeIterator;
// Aggregate object: Binary tree
typedef struct {
TreeNode* root;
TreeIterator* (*create_iterator)(struct BinaryTree* self);
} BinaryTree;
// Tree iterator's has_next
static bool tree_has_next(TreeIterator* self) {
return !is_queue_empty(self->queue);
}
// Tree iterator's next
static int tree_next(TreeIterator* self) {
TreeNode* node = dequeue(self->queue);
if (node->left) enqueue(self->queue, node->left);
if (node->right) enqueue(self->queue, node->right);
return node->value;
}
// Tree iterator's reset (reinitialize queue)
static void tree_reset(TreeIterator* self) {
free_queue(self->queue);
self->queue = create_queue();
if (self->root) enqueue(self->queue, self->root);
}
// Create tree iterator
static TreeIterator* tree_create_iterator(BinaryTree* self) {
TreeIterator* iter = malloc(sizeof(TreeIterator));
iter->has_next = tree_has_next;
iter->next = tree_next;
iter->reset = tree_reset;
iter->root = self->root;
iter->queue = create_queue();
if (self->root) enqueue(iter->queue, self->root);
return iter;
}
// Create tree node
TreeNode* create_tree_node(int value) {
TreeNode* node = malloc(sizeof(TreeNode));
node->value = value;
node->left = node->right = NULL;
return node;
}
// Create binary tree (example: 1
// / \
// 2 3
// / \
// 4 5 )
BinaryTree* create_binary_tree() {
BinaryTree* tree = malloc(sizeof(BinaryTree));
tree->root = create_tree_node(1);
tree->root->left = create_tree_node(2);
tree->root->right = create_tree_node(3);
tree->root->left->left = create_tree_node(4);
tree->root->left->right = create_tree_node(5);
tree->create_iterator = tree_create_iterator;
return tree;
}
// Free binary tree
void free_tree_node(TreeNode* node) {
if (!node) return;
free_tree_node(node->left);
free_tree_node(node->right);
free(node);
}
void free_binary_tree(BinaryTree* tree) {
free_tree_node(tree->root);
free(tree);
}
// Free tree iterator
void free_tree_iterator(TreeIterator* iter) {
free_queue(iter->queue);
free(iter);
}
// Client usage: Level order traversal of binary tree
void traverse(TreeIterator* iter) {
printf("Tree level order traversal: ");
while (iter->has_next(iter)) {
printf("%d ", iter->next(iter));
}
printf("\n");
}
int main() {
BinaryTree* tree = create_binary_tree();
TreeIterator* iter = tree->create_iterator(tree);
traverse(iter); // Output: 1 2 3 4 5 (level order traversal)
iter->reset(iter);
printf("First element after reset: %d\n", iter->next(iter)); // Output: 1
free_tree_iterator(iter);
free_binary_tree(tree);
return 0;
}
The output of the above code is as follows
Tree level order traversal: 1 2 3 4 5
First element after reset: 1
The corresponding UML diagram is as follows

That is, the binary tree iterator implements level order traversal through a queue, allowing the client to traverse without needing to understand the tree structure or traversal algorithm, reflecting the iterator pattern’s encapsulation capability for complex data structures.
Example 5: Filter Iterator (Conditional Traversal)
Implement an iterator with filtering functionality, returning only elements that meet certain conditions (such as even numbers) during array traversal.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Filter condition function pointer
typedef bool (*FilterFunc)(int value);
// Filter iterator
typedef struct {
bool (*has_next)(struct FilterIterator* self);
int (*next)(struct FilterIterator* self);
void (*reset)(struct FilterIterator* self);
// Iterator state
int* data;
int size;
int current;
FilterFunc filter; // Filter function
} FilterIterator;
// Aggregate object: Array supporting filter iteration
typedef struct {
int* data;
int size;
FilterIterator* (*create_filter_iterator)(struct FilterableArray* self, FilterFunc filter);
} FilterableArray;
// Filter iterator's has_next (find the next element that meets the condition)
static bool filter_has_next(FilterIterator* self) {
while (self->current < self->size) {
if (self->filter(self->data[self->current])) {
return true;
}
self->current++;
}
return false;
}
// Filter iterator's next (return the current element that meets the condition and move the pointer)
static int filter_next(FilterIterator* self) {
int value = self->data[self->current];
self->current++;
return value;
}
// Filter iterator's reset
static void filter_reset(FilterIterator* self) {
self->current = 0;
}
// Create filter iterator
static FilterIterator* create_filter(FilterableArray* self, FilterFunc filter) {
FilterIterator* iter = malloc(sizeof(FilterIterator));
iter->has_next = filter_has_next;
iter->next = filter_next;
iter->reset = filter_reset;
iter->data = self->data;
iter->size = self->size;
iter->current = 0;
iter->filter = filter;
return iter;
}
// Create filterable array
FilterableArray* create_filterable_array(int* data, int size) {
FilterableArray* arr = malloc(sizeof(FilterableArray));
arr->data = malloc(sizeof(int) * size);
memcpy(arr->data, data, sizeof(int) * size);
arr->size = size;
arr->create_filter_iterator = create_filter;
return arr;
}
// Free resources
void free_filterable_array(FilterableArray* arr) {
free(arr->data);
free(arr);
}
void free_filter_iterator(FilterIterator* iter) {
free(iter);
}
// Filter condition: Even numbers
static bool even_filter(int value) {
return value % 2 == 0;
}
// Filter condition: Greater than 3
static bool greater_than_3_filter(int value) {
return value > 3;
}
// Client usage: Conditional traversal
void traverse_with_filter(FilterIterator* iter, const char* condition) {
printf("Traversing (filter: %s): ", condition);
while (iter->has_next(iter)) {
printf("%d ", iter->next(iter));
}
printf("\n");
}
int main() {
int data[] = {1, 2, 3, 4, 5, 6, 7, 8};
FilterableArray* arr = create_filterable_array(data, 8);
// Traverse even numbers
FilterIterator* even_iter = arr->create_filter_iterator(arr, even_filter);
traverse_with_filter(even_iter, "even numbers"); // Output: 2 4 6 8
// Traverse numbers greater than 3
FilterIterator* gt3_iter = arr->create_filter_iterator(arr, greater_than_3_filter);
traverse_with_filter(gt3_iter, "numbers > 3"); // Output: 4 5 6 7 8
free_filter_iterator(even_iter);
free_filter_iterator(gt3_iter);
free_filterable_array(arr);
return 0;
}
The output of the above code is as follows
Traversing (filter: even numbers): 2 4 6 8
Traversing (filter: numbers > 3): 4 5 6 7 8
The corresponding UML diagram is as follows

That is, the filter iterator achieves conditional traversal by injecting filter conditions through function pointers, allowing the client to flexibly define filtering rules without modifying the core logic of the iterator or aggregate object.
4. Application of the Iterator Pattern in the Linux Kernel
- 1. Linked List Iterator (list_for_each): The kernel implements a doubly linked list through
<span>struct list_head</span>, and the accompanying<span>list_for_each</span>macro (iterator) encapsulates the linked list traversal logic:struct list_head { struct list_head *next, *prev; }; #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next)The client only needs to traverse using
<span>list_for_each</span>without directly manipulating the<span>next</span>/<span>prev</span>pointers, which is a typical application of the iterator pattern in macro definitions. - 2. Hash Table Iterator (hlist_for_each): The kernel hash table (
<span>struct hlist_head</span>) uses the<span>hlist_for_each</span>macro to traverse the linked list in hash buckets, hiding the details of the hash table’s bucket structure and linked list nodes:#define hlist_for_each(pos, head) \ for (pos = (head)->first; pos; pos = pos->next) - 3. Device Tree Node Iterator (of_iterator): In device tree parsing, the
<span>of_for_each_child_node</span>function is used to traverse child nodes, encapsulating the hierarchical structure traversal of the device tree:struct device_node { struct device_node *parent; struct device_node *child; struct device_node *sibling; }; #define of_for_each_child_node(parent, child) \ for (child = parent->child; child; child = child->sibling) - 4. File System Directory Entry Iterator (struct dir_context): In VFS,
<span>struct dir_context</span>serves as a directory entry iterator, providing an<span>actor</span>callback function to traverse directory entries, hiding the directory storage differences of different file systems (such as ext4, btrfs):struct dir_context { int (*actor)(struct dir_context *, const char *, int, loff_t, u64, unsigned); }; - 5. Process List Iterator (for_each_process): The kernel traverses all processes in the system using the
<span>for_each_process</span>macro, encapsulating the traversal logic of the process list (<span>task_struct->tasks</span>):#define for_each_process(p) \ for (p = &init_task; (p = next_task(p)) != &init_task; )
5. Implementation Considerations
- 1. Lifecycle of Iterators and Aggregate Objects: Iterators typically depend on the existence of aggregate objects, and it is necessary to ensure that the aggregate object is not destroyed during the iterator’s usage; if the aggregate object is modified (such as adding/removing elements), the invalidation of the iterator must be considered (such as marking the iterator as invalid).
- 2. Thread Safety: Iterators do not guarantee thread safety by default; in a multithreaded environment, the iterator state (such as the
<span>current</span>pointer) should be protected using locking mechanisms or use immutable aggregate objects. - 3. Iterator Type Matching: The iterator interfaces of different aggregate objects should remain consistent (such as unified
<span>has_next</span>and<span>next</span>function pointers) to ensure that the client can seamlessly switch iterators. - 4. Performance Optimization: For large aggregate objects (such as arrays with millions of elements), the iterator should avoid redundant calculations during each
<span>next</span>call (such as repeated boundary checks) and can pre-cache some data to improve efficiency. - 5. Avoid Exposing Internal Structures: The core value of the iterator is to hide the internal implementation of the aggregate object; therefore, the iterator interface should not include members related to the structure of the aggregate object (such as the
<span>next</span>pointer of a linked list or the index of an array).
6. Additional Notes
- • The relationship between the iterator pattern and cursors: Cursors are implementations of iterators in the database domain, used to traverse query result sets, consistent with the idea of the iterator pattern.
- • Classification of iterators: By traversal direction, they can be classified into forward iterators and backward iterators; by movement method, they can be classified into random access iterators (such as arrays, supporting
<span>+=n</span>), bidirectional iterators (such as linked lists, supporting<span>++</span>/<span>--</span><code>), and forward iterators (such as singly linked lists, supporting only <code><span>++</span>). - • Applicable scenarios: Scenarios that require unified traversal of different data structures, need to hide the internal implementation of aggregate objects, require support for multiple traversal methods, or conditional traversal.
Through the iterator pattern, C language programs (especially in the Linux kernel) can handle the traversal of various data structures in a unified manner, reducing code coupling and enhancing system maintainability and extensibility. The kernel extensively uses macro definitions to implement lightweight iterators, balancing flexibility and execution efficiency.
Click to read the full article, thank you for sharing, saving, liking, and viewing.