C语言链表是通过指针将零散的内存块串联起来的动态数据结构,每个节点包含数据域和指向下一个节点的指针域,不需要像数组那样提前申请固定大小的内存,适合存储数量不确定的数据。链表的核心操作围绕节点的创建、连接、修改和释放展开,下面以最常用的单链表为例讲解具体使用方法。

链表节点的定义
使用链表前首先需要定义节点结构体,结构体需要包含存储数据的成员和指向下一个同类型节点的指针成员,示例如下:
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点结构体
struct Node {
int data; // 数据域,存储节点数据
struct Node* next; // 指针域,指向下一个节点
};
创建链表与添加节点
创建链表通常需要先创建头节点,头节点一般不存储有效数据,仅用来标识链表的起始位置,之后通过动态内存分配创建新节点并连接到链表中。
// 创建链表并添加3个节点
struct Node* create_list() {
// 创建头节点,分配内存
struct Node* head = (struct Node*)malloc(sizeof(struct Node));
if (head == NULL) {
printf("内存分配失败n");
return NULL;
}
head->next = NULL; // 初始头节点指向空
struct Node* tail = head; // 尾指针,指向当前链表最后一个节点
// 循环添加3个节点
for (int i = 1; i <= 3; i++) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
if (new_node == NULL) {
printf("内存分配失败n");
return head;
}
new_node->data = i * 10; // 设置节点数据
new_node->next = NULL; // 新节点暂时指向空
tail->next = new_node; // 尾节点的next指向新节点
tail = new_node; // 更新尾指针
}
return head;
}
链表遍历输出
遍历链表需要从头节点的下一个节点开始,依次访问每个节点的数据,直到遇到空指针为止。
// 遍历链表并输出所有节点数据
void print_list(struct Node* head) {
struct Node* current = head->next; // 跳过头节点
while (current != NULL) {
printf("%d ", current->data);
current = current->next; // 移动到下一个节点
}
printf("n");
}
插入节点操作
链表插入节点分为头插法和尾插法,也可以指定位置插入,下面示例为在指定值之后插入新节点:
// 在第一个值为target的节点后插入新节点,数据为new_data
void insert_node(struct Node* head, int target, int new_data) {
struct Node* current = head->next;
while (current != NULL) {
if (current->data == target) {
// 创建新节点
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
if (new_node == NULL) {
printf("内存分配失败n");
return;
}
new_node->data = new_data;
// 插入节点:新节点的next指向当前节点的下一个节点,当前节点的next指向新节点
new_node->next = current->next;
current->next = new_node;
return;
}
current = current->next;
}
printf("未找到值为%d的节点,插入失败n", target);
}
删除节点操作
删除节点需要先找到目标节点的前一个节点,修改其next指针跳过要删除的节点,再释放被删除节点的内存。
// 删除第一个值为target的节点
void delete_node(struct Node* head, int target) {
struct Node* prev = head; // 前驱节点,初始为头节点
struct Node* current = head->next; // 当前节点
while (current != NULL) {
if (current->data == target) {
prev->next = current->next; // 前驱节点指向当前节点的下一个节点
free(current); // 释放当前节点内存
printf("成功删除值为%d的节点n", target);
return;
}
prev = current;
current = current->next;
}
printf("未找到值为%d的节点,删除失败n", target);
}
释放链表内存
链表使用完毕后必须手动释放所有节点的内存,避免内存泄漏,释放时需要从头节点开始依次释放每个节点。
// 释放整个链表的内存
void free_list(struct Node* head) {
struct Node* current = head;
while (current != NULL) {
struct Node* temp = current; // 临时保存当前节点
current = current->next; // 移动到下一个节点
free(temp); // 释放临时节点内存
}
printf("链表内存已释放n");
}
完整使用示例
将上面的函数组合起来,即可完成链表的完整使用流程:
int main() {
// 创建链表
struct Node* list = create_list();
if (list == NULL) {
return 1;
}
printf("初始链表数据:");
print_list(list); // 输出:10 20 30
// 插入节点
insert_node(list, 20, 25);
printf("插入后链表数据:");
print_list(list); // 输出:10 20 25 30
// 删除节点
delete_node(list, 10);
printf("删除后链表数据:");
print_list(list); // 输出:20 25 30
// 释放内存
free_list(list);
return 0;
}
使用注意事项
- 每次使用
malloc分配节点内存后,都需要检查返回值是否为NULL,避免内存分配失败导致的空指针错误。 - 操作指针时必须确保指针不为空,避免访问野指针导致程序崩溃。
- 链表使用完毕后一定要调用释放函数,逐个释放所有节点内存,防止内存泄漏。
- 头节点仅作为链表的标识,遍历、插入、删除操作时通常不需要处理头节点本身的数据。