C语言链表字符串分割问题:节点数据损坏及段错误求助
字符串分割链表的段错误与数据损坏问题分析与修复
问题描述
用结构体实现链表按分隔符分割字符串时,第一个字符串可正常输出,但第二个字符串是随机字符,第三个及之后节点触发Segmentation fault(段错误)。代码如下:
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct strliststruct{ char* string; unsigned long long length; struct strliststruct* prior; struct strliststruct* later; int exist_prior; int exist_later; }strlist; strlist init_strlist(){ strlist ans; ans.exist_later=0; ans.exist_prior=0; ans.length=0; return ans; } strlist init_strlist_with_string(char* str,char splitter){ strlist ans=init_strlist(),next; unsigned long long i; for(i=0;1;i++){ if(str[i]==splitter){ next=init_strlist_with_string(&str[i+1],splitter); ans.later=&next; ans.exist_later=1; next.exist_prior=1; next.prior=&ans; }else if(str[i]!='\0'){continue;} ans.string=malloc(sizeof(char)*(i+1)); ans.string[i]='\0'; for(unsigned long long j=0;j<i;j++){ ans.string[j]=str[j]; } break; } return ans; } int main(){ strlist sl=init_strlist_with_string("I am a human",' '); while(1){ printf("%s\n",sl.string); if(sl.exist_later){sl=*sl.later;}else break; } return 0; }
核心错误原因
- 局部变量指针悬空:
init_strlist_with_string函数中,next是栈上的局部变量,将&next赋值给ans.later后,函数返回时next的栈内存会被释放,ans.later变成野指针。后续访问*sl.later时,读取的是已经被回收的内存,导致数据错乱或段错误。 - 栈内存滥用:所有链表节点都是栈上的局部变量,而非堆内存分配。递归调用结束后,栈帧被销毁,节点内存失效,后续访问必然触发非法内存操作。
- 未初始化关键字段:
length字段未被赋值,属于逻辑漏洞,可能影响后续链表操作的正确性。
修复方案与代码
将链表节点改为堆内存分配,调整递归逻辑以正确维护指针关系,同时补全length字段的赋值:
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct strliststruct{ char* string; unsigned long long length; struct strliststruct* prior; struct strliststruct* later; int exist_prior; int exist_later; }strlist; // 初始化单个节点(堆分配) strlist* init_strlist(){ strlist* ans = malloc(sizeof(strlist)); if (!ans) return NULL; ans->exist_later = 0; ans->exist_prior = 0; ans->length = 0; ans->string = NULL; ans->prior = NULL; ans->later = NULL; return ans; } // 递归分割字符串并创建链表 strlist* init_strlist_with_string(char* str, char splitter){ strlist* ans = init_strlist(); if (!ans) return NULL; unsigned long long i = 0; // 找到分隔符或字符串结束位置 while (str[i] != splitter && str[i] != '\0') { i++; } // 分配并复制当前段字符串 ans->string = malloc(sizeof(char) * (i + 1)); if (!ans->string) { free(ans); return NULL; } strncpy(ans->string, str, i); ans->string[i] = '\0'; ans->length = i; // 如果不是字符串结尾,递归创建后续节点 if (str[i] == splitter) { ans->later = init_strlist_with_string(&str[i+1], splitter); if (ans->later) { ans->exist_later = 1; ans->later->exist_prior = 1; ans->later->prior = ans; } } return ans; } // 遍历链表并释放内存 void free_strlist(strlist* head){ strlist* temp; while (head) { temp = head; head = head->later; free(temp->string); free(temp); } } int main(){ strlist* sl = init_strlist_with_string("I am a human", ' '); strlist* current = sl; while (current) { printf("%s\n", current->string); current = current->later; } free_strlist(sl); return 0; }
修复说明
- 堆内存分配节点:所有链表节点通过
malloc在堆上分配,避免栈内存回收导致的野指针问题。 - 修正指针关系:递归创建的后续节点直接赋值给当前节点的
later字段,指针指向堆内存,不会失效。 - 补全字段初始化:正确设置
length字段,并新增内存释放函数free_strlist,避免内存泄漏。 - 简化字符串复制逻辑:用
strncpy替代手动循环复制,代码更简洁可靠。
内容的提问来源于stack exchange,提问作者Altus83765
相关产品推荐
相关产品推荐

