You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中strcat越界拼接为何未覆盖相邻栈变量a仍输出20?

原因分析

测试复现代码

char str[] = "some short string";
int a = 20;
strcat(str, "a very very long string");
printf("%d\n", a); // prints 20

首先得明确:C语言从来没有在标准里规定过栈上局部变量的排布规则,你默认的「int a紧邻str数组的栈高位地址」只是基于栈增长方向的惯性假设,根本不具备通用性。出现越界写入后a的值仍然是20的情况,通常是以下几个原因导致的:

  • 内存对齐预留了足够的空隙
    所有主流平台的栈分配都遵循地址对齐规则,一般按16字节、32字节甚至更大的粒度分配内存,不会严格卡着变量的实际大小切分空间。你定义的str初始内容"some short string"算上结尾的\0总共才18字节,编译器通常会给它分配对齐到下一个边界的整块空间(比如32字节、48字节),数组末尾到下一个变量之间本来就有十几到几十字节的空闲间隙。只要strcat追加的字符串长度没超出这个间隙,越界写入根本碰不到a的内存。
  • 变量实际排布顺序和你想的相反
    很多编译器不会按照代码里的变量定义顺序从低到高分配栈地址,反而会主动调整顺序:把字符数组这类容易溢出的缓冲区放到高地址,普通变量放到更低的地址位置。这种情况下strcat往数组尾部(高地址方向)写越界内容,写的是栈上更靠上、离a更远的冗余空间,完全碰不到a的存储位置。
  • 编译器优化直接把a从栈上消掉了
    如果编译时开了O1及以上优化,编译器做数据流分析就能发现a从赋值到printf调用之间从来没被修改过,根本不会给a在栈上分配实际内存,生成printf调用代码时直接把常量20当参数传进去。这种情况下不管栈上怎么越界写,都影响不了硬编码在指令里的常量值。
  • 栈保护机制做了隔离
    现在主流编译器默认开栈溢出保护(比如GCC的-fstack-protector),会在字符数组这类易溢出的缓冲区和其他局部变量之间插一段随机的canary校验值,占几个到十几个字节的空间。如果溢出长度刚好只覆盖了缓冲区和canary之间的填充位,没篡改canary和其他变量,程序就不会触发溢出崩溃,a的值也能保持不变。

提醒一句:栈缓冲区溢出属于C标准明确规定的未定义行为,不管程序最终是输出20、输出乱码、直接崩溃还是出现其他任何表现,都是标准允许的结果,绝对不能靠这类偶然的运行结果写代码。

内容的提问来源于stack exchange,提问作者Scrapper142

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:22:47