C语言中使用printf时fgets获取的字符串数据丢失问题求助
兄弟,我一眼就揪出你代码里的关键问题了!你遇到的字符串打印异常、数据丢失的诡异情况,根源全在get_string()这个函数里!
问题核心:返回局部变量的指针导致野指针
你看这个函数:
char *get_string() { char str[100]; puts("Please enter a string"); return fgets(str, sizeof(str), stdin); }
这里的str是局部数组,它存在函数的栈帧里。当get_string()执行完毕返回时,这个栈帧会被销毁,str占用的内存会被操作系统回收或者后续函数调用覆盖。你返回的指针就变成了「野指针」——指向的内存已经不属于你的程序了!
后续你用carray = get_string();拿到这个野指针后,第一次printf可能刚好内存还没被修改,能勉强打出内容;但后面执行了其他函数(比如打印菜单、读取用户选择的fgets),这块内存被覆盖了,第二次printf自然就看不到正确的字符串了,甚至可能出现更奇怪的崩溃行为,这都是C语言里的未定义行为。
给你两种靠谱的解决方案
方案1:让调用方提供缓冲区(推荐,避免内存泄漏)
把get_string()改成接收外部传入的缓冲区和大小,这样内存的生命周期由调用方控制:
// 修改get_string函数 char *get_string(char *buf, size_t buf_size) { puts("Please enter a string"); return fgets(buf, buf_size, stdin); }
然后在main里调整你的代码:
// 先把原来的functions数组改大,5个长度太小,用户输入容易溢出 char functions[100] = ""; char *carray = functions; // 当选择操作0时,这样调用 if (opNum == 0) get_string(carray, sizeof(functions)); // 直接用main里的缓冲区
这样缓冲区是在main的栈上,只要main没结束,内存就一直有效,不会出现野指针问题。
方案2:用动态内存分配(记得要释放内存)
如果一定要在get_string()里分配内存,就用malloc在堆上申请,堆内存不会随函数返回释放:
char *get_string() { // 申请100字节的堆内存 char *str = malloc(100 * sizeof(char)); if (str == NULL) { perror("malloc failed"); // 处理内存分配失败的情况 return NULL; } puts("Please enter a string"); // 如果fgets读取失败,要释放内存避免泄漏 if (fgets(str, 100, stdin) == NULL) { free(str); return NULL; } return str; }
但要注意,用了malloc就必须在不需要这个字符串的时候调用free(carray);,比如在程序结束前:
// 在main的末尾,break之后或者return之前 if (carray != functions) { // 只有动态分配的才需要free free(carray); }
不然会造成内存泄漏。
额外提醒:别忽略缓冲区大小的问题
你原来定义的char functions[5] = "";长度只有5,用户输入的字符串如果超过3个字符(加上换行符和字符串终止符\0)就会缓冲区溢出,这也是未定义行为的来源之一,一定要把缓冲区设得足够大,比如100,和fgets的读取长度匹配。
备注:内容来源于stack exchange,提问作者Din Yair Sadot

