为何sscanf会修改非目标数据?C语言技术问题求助
问题原因分析
其实这个问题的核心是指针指向了栈上的局部变量,导致后续操作覆盖了之前的内容,具体来说:
- 你的
addent函数只是把传入的id_ent指针直接赋值给nodo_id的ident成员,而id_ent是main函数里的temp2——这是一个在栈上分配的局部数组,它的内存地址是固定不变的(只要main函数在运行)。 - 第一次调用
addent(temp2)后,testa_e->ident指向的是temp2的内存地址;第二次循环时,sscanf把新的字符串secondword写入temp2,这就直接覆盖了temp2里原来的firstword。因为第一个节点的ident还是指向temp2的地址,所以打印出来的内容自然就变了。 - 另外你的代码还有一个隐藏bug:
addent的else分支里,循环条件是while (curs != NULL),这会让curs最终变成NULL,之后malloc的新节点根本没有被链接到链表上——也就是说你输入第二次addent时,新节点其实没被加到链表中,只是覆盖了temp2的内容而已。
修复方法及代码示例
要解决这个问题,关键是让每个链表节点拥有独立的字符串内存,而不是共享外部的局部变量内存,同时还要修复链表添加的逻辑错误。
方法1:使用strdup复制字符串(最简单)
strdup会自动为字符串分配内存并复制内容,注意它是POSIX标准函数,如果你的环境不支持,可以用malloc+strcpy替代。修改后的addent函数如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define string 30 #define line 100 typedef const char *identifier; struct nodo_id { identifier ident; struct nodo_id *next; }; typedef struct nodo_id nodo_id; nodo_id *testa_e = NULL; void addent(const char *id_ent) { if (testa_e == NULL) { testa_e = malloc(sizeof(nodo_id)); // 复制字符串到新分配的内存 testa_e->ident = strdup(id_ent); if (testa_e->ident == NULL) { perror("strdup failed"); free(testa_e); exit(EXIT_FAILURE); } testa_e->next = NULL; } else { nodo_id *curs = testa_e; // 修复循环条件:找到最后一个节点(而不是走到NULL) while (curs->next != NULL) { curs = curs->next; } curs->next = malloc(sizeof(nodo_id)); curs->next->ident = strdup(id_ent); if (curs->next->ident == NULL) { perror("strdup failed"); free(curs->next); exit(EXIT_FAILURE); } curs->next->next = NULL; } } int main() { char temp[line]; char temp2[string]; char temp5[string]; fgets(temp, line, stdin); while (strncmp(temp, "end", 3) != 0) { if (strncmp(temp, "addent", 6) == 0) { if (testa_e != NULL) printf("\nbefore sscanf: %s\n", testa_e->ident); sscanf(temp, "%s %s", temp5, temp2); if (testa_e != NULL) printf("\nafter sscanf: %s\n", testa_e->ident); addent(temp2); } fgets(temp, line, stdin); } }
方法2:手动用malloc+strcpy复制字符串
如果你的环境不支持strdup,可以手动分配内存并复制:
把strdup(id_ent)替换成:
// 分配足够的内存(字符串长度+1,用来存结束符'\0') testa_e->ident = malloc(strlen(id_ent) + 1); if (testa_e->ident == NULL) { perror("malloc failed"); free(testa_e); exit(EXIT_FAILURE); } strcpy(testa_e->ident, id_ent);
额外说明
修复后,每个节点的ident都会指向一块独立的堆内存,后续修改temp2不会影响之前节点的内容,同时链表的添加逻辑也正确了——第二次输入的secondword会被正确添加为第二个节点。
最后别忘了,程序结束时要遍历链表,释放每个节点的ident和节点本身的内存,避免内存泄漏哦~
内容的提问来源于stack exchange,提问作者wattbatt
相关产品推荐
相关产品推荐

