C语言链表存储文件单词时遍历打印仅重复最后单词问题
问题根因
你误以为问题出在链表打印环节,实际上打印逻辑本身没有错误,核心问题是所有链表节点的data指针都指向了同一个栈上的buffer数组,你根本没有为每个读取到的单词单独分配存储内存。
你在add函数中写的new_node->data = data;仅仅是把传入的buffer首地址存到了节点里,所有节点的data都指向同一块内存地址。读取文件时你不断往buffer里写入新单词覆盖旧内容,调用add时打印正常是因为当下buffer里刚好存的是刚读到的单词,等全部文件读取完成后,buffer里留存的只有最后一个单词work?,此时遍历链表访问所有节点的data指针,指向的都是这块存了最后一个单词的内存,自然会重复输出最后一个词,附带异常符号是因为部分场景下字符串没有正确补\0结束符。
修复方案
按以下顺序修改代码即可解决问题:
- 修改add函数逻辑,为每个单词单独分配堆内存,拷贝字符串内容,不要直接存储buffer的地址
将add函数中new_node->data = data;替换为如下代码:
new_node->data = malloc(strlen(data) + 1); // 多分配1字节存字符串结束符\0 strcpy(new_node->data, data);- 修改add函数逻辑,为每个单词单独分配堆内存,拷贝字符串内容,不要直接存储buffer的地址
- 修改free_list函数,释放节点时先释放节点内存储单词的内存,避免内存泄漏
原逻辑只释放了节点结构体本身的内存,没有释放存字符串的堆内存,修改后代码:
void free_list(ptr *hnode) { ptr p; while(*hnode){ p = *hnode; *hnode = (*hnode)->next; free(p->data); free(p); } }- 修改free_list函数,释放节点时先释放节点内存储单词的内存,避免内存泄漏
- 修复读取逻辑的隐藏bug
fgetc返回值是int类型,把原来的char ch;改成int ch;,否则在char为无符号类型的平台上EOF判断会失效- 去掉
while(!feof(file))的判断写法,feof只有在读取操作已经越过文件末尾后才会返回真,会导致多读取异常字符,直接用fgetc的返回值判断EOF即可 - 增加连续分隔符判断,避免连续空格、空行生成内容为空的无效节点
- 循环结束后补充处理文件末尾的最后一个单词——如果文件最后一个单词后没有空格/换行,原逻辑会漏掉这个单词
修复后的文件读取核心代码参考:
int ch; int i = 0; while( (ch = fgetc(file)) != EOF && i < MAX-1 ) { if(ch == ' '|| ch == '\n' || ch == '\t') { if(i > 0) { buffer[i] = '\0'; add(buffer); i = 0; } } else{ buffer[i++] = ch; } } // 处理文件末尾最后一个未被分隔符截断的单词 if(i > 0) { buffer[i] = '\0'; add(buffer); }
内容的提问来源于stack exchange,提问作者Zimroie
相关产品推荐
相关产品推荐

