C语言用户访问统计程序报栈缓冲区溢出警告但运行正常,原因是什么?
问题分析
溢出点位定位
- 第一个明确溢出点:
scanf("%s", &sign)语句。sign是单字节char类型,%s是字符串读取格式符,读取到输入字符后会自动在末尾追加空终止符\0,至少会写入2字节数据,直接超出sign仅1字节的内存空间,产生栈溢出。 - 第二个溢出风险:栈上分配超大数组。
userslogginrank数组大小为1000000个int,按4字节/int计算占4MB,usersvisitations数组大小为100000个int占400KB,合计近4.4MB。而Windows系统默认栈空间仅1MB,就算是Linux系统默认栈空间也只有8MB,很容易超出栈容量限制触发栈溢出。 - 第三个溢出风险:循环终止条件错误。do-while的终止条件写为
end != 1 || i <= 999999,逻辑或的特性决定了就算end被置为1,只要i不超过999999就会继续循环,当i达到VISIT_MAX(1000000)之后继续写入userslogginrank[i]就会产生数组越界溢出。 - 第四个潜在问题:局部数组未初始化。
usersvisitations和userslogginrank都是栈上局部变量,没有手动初始化,其初始值是栈上残留的随机值,访问未写入的位置属于未定义行为。
程序仍可正常运行的原因
栈缓冲区溢出属于C语言的未定义行为,未定义行为不代表一定会崩溃,只是行为不受C标准约束,你遇到的正常运行只是特定场景下的巧合:
scanf("%s", &sign)溢出的1字节数据如果刚好写到了栈上未被使用的内存区域,或者覆盖了不重要的临时数据,没有破坏函数返回地址、栈帧指针等关键数据,就不会触发崩溃。- 如果你使用的是Linux/macOS等系统,默认栈空间大小为8MB,4.4MB的数组可以完全放下,不会触发栈空间不足的溢出。
- 运行程序时栈上残留的内存刚好是0值,所以
usersvisitations的计数不会出现异常值,表现为功能正常。 - 你的测试输入量比较小,没有触发数组越界的场景,所以越界问题没有暴露。
修复建议
- 将
scanf("%s", &sign)改为scanf(" %c", &sign),用%c读取单个字符,前面加空格匹配输入中的空白符。 - 把超大数组改为全局变量,或者用
malloc动态分配内存,全局变量和动态分配的内存位于堆区,不受栈大小限制。 - 修正循环终止条件为
end != 1 && i < VISIT_MAX,避免数组越界。 - 初始化
usersvisitations数组所有元素为0,保证计数逻辑正确。
内容的提问来源于stack exchange,提问作者ondra hofmann
相关产品推荐
相关产品推荐

