C语言字符串问题:未初始化访问与栈缓冲区溢出排查求助
回文字符串检测代码的栈溢出与未初始化访问问题解决
问题场景
编写回文检测代码时,明明两个字符串内容一致,却触发栈缓冲区溢出和未初始化变量访问错误。手动添加空终止符、改用fgets均未解决问题。原代码及错误信息如下:
原代码
#include <stdio.h> #include <string.h> #include <ctype.h> #define MAX 4096 int main(void) { // Handle taking in input char str[MAX]; int len = 0; while (scanf("%c", &str[len]) != EOF) { len++; } char str_rev[MAX]; int j = 0; for (int i = len-1; i >= 0; i--) { str_rev[j] = str[i]; j++; } if (strcmp(str, str_rev) == 0) { printf("String is a palindrome\n"); } else { printf("String is not a palindrome\n"); } return 0; }
错误信息与变量状态
错误信息:stack buffer overflow.
执行停止时变量值:j = 7 len = 7 str = "racecar", <4089 uninitialized values> str_rev = "racecar", <4089 uninitialized values> str_rev[j] = <uninitialized value>
核心原因
- 缺少字符串空终止符
\0:C语言中,strcmp等字符串操作函数依赖末尾的\0判断字符串结束位置。原代码中:str通过scanf逐个读取字符,未手动添加\0;str_rev完成字符反转后,也未添加\0。
这导致strcmp会持续读取栈上未初始化的内存区域,既触发未初始化访问警告,也可能因越界读取触发栈溢出。
- 输入无长度限制:
while循环未检查len是否超过MAX-1,当输入字符数超过缓冲区容量时,会直接越界写入str的栈空间,触发栈溢出。 - 手动加终止符无效的可能原因:未在正确位置添加——比如未给
str[len]和str_rev[j](即str_rev[len])赋值\0,或添加后又被后续输入覆盖。
修复方案
方案1:修正原scanf逻辑
添加长度限制和空终止符:
#include <stdio.h> #include <string.h> #include <ctype.h> #define MAX 4096 int main(void) { char str[MAX]; int len = 0; // 限制输入长度,预留1个位置给空终止符 while (len < MAX - 1 && scanf("%c", &str[len]) != EOF) { len++; } // 给str添加字符串结束符 str[len] = '\0'; char str_rev[MAX]; int j = 0; for (int i = len-1; i >= 0; i--) { str_rev[j] = str[i]; j++; } // 给str_rev添加字符串结束符 str_rev[j] = '\0'; if (strcmp(str, str_rev) == 0) { printf("字符串是回文\n"); } else { printf("字符串不是回文\n"); } return 0; }
方案2:改用fgets并处理换行符
若使用fgets需注意它会读取换行符,需手动剔除:
#include <stdio.h> #include <string.h> #include <ctype.h> #define MAX 4096 int main(void) { char str[MAX]; int len = 0; // 用fgets读取输入,自动添加空终止符 if (fgets(str, MAX, stdin) != NULL) { len = strlen(str); // 去掉末尾的换行符(如果存在) if (len > 0 && str[len-1] == '\n') { str[len-1] = '\0'; len--; } } char str_rev[MAX]; int j = 0; for (int i = len-1; i >= 0; i--) { str_rev[j] = str[i]; j++; } str_rev[j] = '\0'; if (strcmp(str, str_rev) == 0) { printf("字符串是回文\n"); } else { printf("字符串不是回文\n"); } return 0; }
同类问题排查要点
- 强制检查空终止符:所有需用字符串函数处理的字符数组,必须确保末尾有
\0,调试时可直接查看数组内存确认。 - 严格限制缓冲区边界:任何写入数组的操作,必须判断索引是否小于数组长度-1(预留
\0的位置),禁止越界写入。 - 明确输入函数行为:
scanf("%c")会读取所有字符(包括换行、空格),不会自动加\0;fgets会读取到换行符或缓冲区满,自动添加\0,但需手动处理不需要的换行符。
- 调试时关注内存状态:通过调试工具查看数组的内存布局,确认是否存在未初始化区域、越界写入的痕迹。
内容的提问来源于stack exchange,提问作者Markus H
相关产品推荐
相关产品推荐

