C语言单链表先调用insrt_frnt不生效、先调用insrt_end正常的原因是什么?
核心错误总览
你的代码存在3个致命逻辑/语法错误,三者共同导致了「先调用头插崩溃、先调用尾插能正常运行」的现象:
- 内存申请大小错误,访问非法内存
- 头插函数节点初始化缺失,产生野指针
- 尾插函数遍历逻辑错误,存在空指针解引用风险
错误细节说明
1. 内存申请大小错误
你两处malloc的参数都写的是sizeof(struct node *),这里申请的是结构体指针的大小(32位系统占4字节,64位系统占8字节),远小于struct node结构体本身需要的空间。后续给data、next成员赋值时会访问到不属于当前节点的内存,属于未定义行为,运行结果完全随机,刚好先调用尾插时非法访问没触发系统内存保护,就看起来能正常运行,先调用头插时触发保护就直接崩溃。
正确写法应该是申请结构体本身的大小:
// 旧写法(错误) struct node *temp = (struct node *) malloc(sizeof(struct node *)); // 新写法(正确) struct node *temp = (struct node *) malloc(sizeof(struct node));
2. 头插函数初始化逻辑缺失
你的insrt_frnt函数只有head != NULL时才给temp->next赋值,当第一次调用头插、head为NULL时,没有给temp->next赋值,导致temp->next是野指针,后续遍历链表到这里时就会访问非法内存崩溃。
而你先调用尾插时,尾插函数会主动给新节点的next赋值为NULL,就不会出现野指针问题,所以看起来能正常运行。
正确的头插逻辑可以直接简化,不需要单独判断head是否为空:
struct node * insrt_frnt(struct node *head){ struct node *temp = (struct node *) malloc(sizeof(struct node)); printf("\n Enter data:"); scanf("%d", &temp->data); // 统一给新节点的next赋值,不管head是否为空 temp->next = head; // 新的头节点就是temp return temp; }
3. 尾插函数遍历逻辑错误
你尾插函数的循环条件是while(ptrr != NULL),跳出循环时ptrr已经是空指针,此时执行ptrr->next = neww属于空指针解引用,100%会触发非法访问崩溃。你之前先调用尾插能跑是因为第一次调用时head为NULL,直接走了if(head == NULL)的分支,没执行到后面的循环逻辑而已,只要链表有1个以上节点再调用尾插就会直接崩溃。
正确的遍历逻辑应该是找到最后一个节点(即ptrr->next == NULL时停止):
struct node * insrt_end(struct node *head){ struct node *ptrr = head; struct node *neww = (struct node *) malloc(sizeof(struct node)); printf("\n Enter data:"); scanf("%d", &neww->data); neww->next = NULL; if(head == NULL){ head = neww; } else { // 找最后一个节点,不是找空指针 while(ptrr->next != NULL){ ptrr = ptrr->next; } ptrr->next = neww; } return head; }
修正后完整代码
#include <stdio.h> #include <stdlib.h> struct node { int data; struct node *next; }; struct node * insrt_frnt(struct node *head){ struct node *temp = (struct node *) malloc(sizeof(struct node)); printf("\n Enter data:"); scanf("%d", &temp->data); temp->next = head; return temp; } struct node * insrt_end(struct node *head){ struct node *ptrr = head; struct node *neww = (struct node *) malloc(sizeof(struct node)); printf("\n Enter data:"); scanf("%d", &neww->data); neww->next = NULL; if(head == NULL){ head = neww; } else { while(ptrr->next != NULL){ ptrr = ptrr->next; } ptrr->next = neww; } return head; } void display(struct node *head){ while(head != NULL){ printf("%d\n", head->data); head = head->next; } } int main(){ struct node *head = NULL; head = insrt_frnt(head); head = insrt_end(head); display(head); // 实际使用时记得补充链表内存释放逻辑避免内存泄漏 return 0; }
内容的提问来源于stack exchange,提问作者Raghav Sharma

