C语言单链表删除首节点函数误删第二个节点问题排查求助
问题现象
C语言实现单链表删除首节点功能时,执行删除逻辑后实际被删除的是第二个节点,而非预期的首节点。
相关代码片段
1. 节点结构体定义
#include <stdio.h> #include <stdlib.h> #include <conio.h> /* Node structure */ typedef struct node { int data; struct node *next; } Node;
2. 链表创建函数
/* Create the linked list */ void create_list(Node *head) { char check; int i=1, num; char * temp; printf("Input data for node 1: "); scanf("%d", &num); head->data = num; printf("\nEnter more <y>es/<n>o ? "); scanf(" %c", &check); while(check=='y') { head -> next = (Node *) malloc(sizeof(Node)); head = head->next; printf("\nInput data for node %d: ",i+1); scanf("%d", &head->data); i++; head->next = NULL; printf("\nEnter more <y>es/<n>o ? "); if (scanf(" %c", &check)!= 1) { printf(" Error: Invalid Input \n"); scanf(" %s", &temp); /* Clear input buffer */ continue; } } }
3. 存在逻辑错误的首节点删除函数
/* Delete first location*/ void delete_first(Node *head) { Node *prev_node; prev_node = head;//assign the prev pointer to beginning of the list head = head->next;//shift the node to next if(head == NULL)//condition for no elements in list printf("\n The List is empty "); else { //logic for deletion prev_node->next = head->next; free(head); //delete the node } }
4. 主函数
int main() { Node *head, start; start.next = NULL; head = &start; create_list(head); head = &start; delete_first(head); }
错误根因排查
问题核心出在delete_first函数的逻辑错误,和链表创建函数无关,具体问题点如下:
- 删除逻辑完全写反:函数内先将
prev_node指向真正的首节点(传入的原始head),紧接着把局部变量head移动到第二个节点位置。后续执行prev_node->next = head->next时,实际是把首节点的next指针直接指向第三个节点,再调用free(head)释放的是已经指向第二个节点的指针,最终表现为第二个节点被删除,首节点残留。 - 空判断位置错误:逻辑上先移动head指针再判断空,如果链表仅存1个节点,
head = head->next会得到NULL,后续访问head->next会直接触发空指针崩溃。 - 参数传递方式错误:C语言函数参数为值传递,
delete_first中修改的是函数内的局部head副本,函数执行结束后主函数的head指针仍指向旧的首节点地址,就算删除逻辑正确,外部头指针也不会同步更新。 - 链表创建函数存在隐藏内存错误:定义的
char *temp是未初始化的野指针,输入非法时执行scanf(" %s", &temp)会向随机内存地址写入数据,可能触发程序崩溃。
修复方案
1. 修正首节点删除逻辑
需要用二级指针传入头节点地址,才能真正修改外部的头指针,同时调整删除逻辑顺序:
/* Delete first location*/ void delete_first(Node **head) { // 先判断链表是否为空,不要先移动指针 if(*head == NULL) { printf("\n The List is empty "); return; } Node *to_delete = *head; // 记录要删除的首节点地址 *head = (*head)->next; // 把头指针移动到下一个节点 free(to_delete); // 释放原来的首节点 }
2. 修正主函数调用方式
传入头指针的地址即可:
int main() { Node *head, start; start.next = NULL; head = &start; create_list(head); head = &start; delete_first(&head); // 传入头指针的地址 }
3. 修复创建函数的野指针问题
将野指针改为固定长度的字符数组用于清空输入缓存:
/* Create the linked list */ void create_list(Node *head) { char check; int i=1, num; char temp[32]; // 替换原来的野指针 printf("Input data for node 1: "); scanf("%d", &num); head->data = num; head->next = NULL; // 补全首节点的next初始化 printf("\nEnter more <y>es/<n>o ? "); scanf(" %c", &check); while(check=='y') { head -> next = (Node *) malloc(sizeof(Node)); head = head->next; printf("\nInput data for node %d: ",i+1); scanf("%d", &head->data); i++; head->next = NULL; printf("\nEnter more <y>es/<n>o ? "); if (scanf(" %c", &check)!= 1) { printf(" Error: Invalid Input \n"); scanf("%s", temp); /* 安全清空输入缓冲区 */ check = 'n'; } } }
边界提示:当前代码的首节点是栈上分配的
start变量,如果链表只有这一个节点,free栈上的内存会触发非法访问,生产环境实现链表建议头节点也用堆分配,或者单独处理首节点是栈变量的边界场景。
内容的提问来源于stack exchange,提问作者Nory Levi
相关产品推荐
相关产品推荐

