C语言实现链表pop操作在长度为1时触发段错误问题排查
C语言单链表pop函数长度为1时触发段错误问题
问题现象
使用C语言实现单链表相关功能时,append(尾插元素)、len(获取链表长度)、print_list(打印链表)函数均可正常运行,但pop(尾删元素)函数存在异常:链表长度大于1时pop执行正常,仅当链表长度为1时执行pop会触发Segmentation fault(段错误)。
复现场景
测试输入第一行指定操作总数为20,依次执行以下操作:
- append 1
- append 2
- append 3
- print(正常输出
1 2 3) - pop
- print(正常输出
1 2) - pop
- print(正常输出
1) - 再次执行pop时终端返回
Segmentation fault: 11错误。
已验证两种临时修改方式可让长度为1时的pop正常运行:
- 注释掉pop函数中处理长度大于2场景的逻辑块
- 在
len()==1的处理分支(将PythonListHead置为NULL)后添加return语句
完整问题代码
#include<stdio.h> #include<stdlib.h> #include<string.h> struct Node { int data; struct Node* next; }; // create a node with data as x struct Node* create_node(int x) { struct Node* ptr = malloc(sizeof(struct Node)); ptr->next = NULL; ptr->data = x; return ptr; } // delete the node at `ptr` and free its memory void delete_node(struct Node* ptr) { free(ptr); } struct Node* PythonListHead = NULL; // prints the list in space-separated format void print_list(struct Node* head) { struct Node* cur = head; while(cur) { printf("%d ", cur->data); cur = cur->next; } printf("\n"); } // Add an item to the end of the list void append(int x) { struct Node *cursor=PythonListHead; struct Node* a=create_node(x); if (PythonListHead==NULL){ PythonListHead=create_node(x); } else{ while(cursor->next!=NULL){ cursor=cursor->next; } cursor->next=a; } } // Return the number of elements in the list int len() { struct Node* last=PythonListHead; int count=0; while (last->next!=NULL){ count++; last=last->next; } return count+1; } // Remove the item at the end of the list void pop() { if (len()==1){ PythonListHead=NULL; } if (len()==2){ delete_node(PythonListHead->next); PythonListHead->next=NULL; } else{ struct Node* cursor=PythonListHead; int count=0; while (cursor){ cursor=cursor->next; count++; if (count==(len()-2)){ delete_node(cursor->next); cursor->next=NULL; } } } } int main(int argc, char const *argv[]) { int T; scanf("%d", &T); char operation_type[20]; int indices[100]; while(T--) { scanf("%s", operation_type); if(strcmp(operation_type, "append") == 0) { int x; scanf("%d", &x); append(x); } if(strcmp(operation_type, "pop") == 0) { pop(); } if(strcmp(operation_type, "len") == 0) { int length = len(); printf("%d\n", length); } if(strcmp(operation_type, "print") == 0) { print_list(PythonListHead); } } }
核心疑问
按照逻辑判断,当len()==1时本不应该进入后续处理长度为2、长度大于2的分支,为什么依然会触发段错误?
故障成因
段错误的直接原因是分支判断写法错误+空指针解引用,具体触发流程如下:
pop函数使用了3个独立的if语句做分支判断,而非互斥的if-else if-else结构,也没有在匹配到分支后提前return终止函数,导致前一个分支修改链表状态后,后续判断逻辑会继续执行。- 当链表长度为1进入pop逻辑时:
- 首先进入第一个
if (len()==1)分支,代码将全局头指针PythonListHead设置为NULL,但既没有释放原头节点的内存(存在内存泄漏),也没有退出函数。 - 代码继续向下执行第二个
if (len()==2)判断,此时会再次调用len()函数:len()函数中直接将局部指针last赋值为已经是NULL的PythonListHead,紧接着在while循环中执行last->next访问空指针的成员,直接触发段错误,根本走不到长度判断的步骤。
- 首先进入第一个
额外存在的代码问题
len()函数本身存在空指针风险:当链表为空(PythonListHead == NULL)时,直接访问last->next必然崩溃,正常实现需要先判断头指针是否为空,为空直接返回0。append函数存在内存泄漏:无论链表是否为空,函数开头都会创建一次节点a,但链表为空时又调用了一次create_node(x)给头指针赋值,之前创建的a没有被释放也没有被使用,直接造成内存泄漏。pop函数长度为1的分支没有释放原头节点内存,会造成内存泄漏。pop函数遍历尾节点时反复调用len()遍历链表,时间复杂度为O(n²),可以通过一次遍历直接找到倒数第二个节点优化性能。
内容的提问来源于stack exchange,提问作者Tatai
相关产品推荐
相关产品推荐

