从内存角度分析C语言偏移int地址定义字符串报错的原因
问题说明
已知前提假设:局部变量内存地址按从高地址到低地址方向分配。
原实现思路:
- 通过char类型指针实现字符串存储,定义char指针并初始化为
&a - 4,预期该指针指向int型变量a低地址方向偏移4字节的位置 - 在
scanf()中使用"%4s"格式控制符限制最多读取4个字符,认为读取的4个字符会依次存入&a-4、&a-3、&a-2、&a-1对应的内存地址
测试代码如下:
int main() { int a = 5; char *string = &a - 4; scanf("%4s", string); printf("%s", string); return 0; }
代码运行时触发异常错误,以下为内存层面的具体原因分析。
内存层面错误原因
代码存在三个核心内存问题,直接触发异常:
- 指针运算逻辑完全错误,偏移量远超预期
C语言的指针偏移以指针指向的类型长度为单位,而非按字节计算偏移。&a是int*类型指针,常规编译环境下sizeof(int)为4字节,因此&a - 4的实际偏移量是4 * sizeof(int) = 16字节,完全不是预期的4字节偏移。这个地址大概率已经跳出当前main函数的栈帧范围,指向未被程序分配写入权限、甚至未映射到进程地址空间的内存,scanf执行写入操作时会直接触发段错误。 - 对
%4s格式符的行为理解错误,必然发生内存越界写入
就算修正指针偏移逻辑,把地址计算改为按字节偏移的(char*)&a - 4,对%4s的行为认知也存在偏差:%4s中的数字4仅限制最多读取4个非空白输入字符,但scanf会自动在读取到的字符序列末尾追加1个'\0'作为字符串终止符,也就是说实际会往指定内存地址写入5字节数据,比预期多1字节。多写的终止符会越界覆盖相邻内存,如果相邻位置存放了栈保护校验值(canary)、函数返回地址这类关键数据,会直接被编译器插入的栈保护逻辑检测到,触发程序异常终止。 - 手动计算局部变量偏移的行为本身属于未定义行为
默认的局部变量内存布局假设本身就不具备可移植性,甚至在同一编译器下不同优化等级都可能变化。C标准从未规定局部变量的栈分配顺序、栈对齐规则,编译器在编译阶段可能对变量位置做重排、插入对齐填充字节、栈保护字段,手动硬编码偏移计算地址的行为本身不受语言规范保障,访问这类地址的结果完全不可预期,出现内存错误是必然结果。
内容的提问来源于stack exchange,提问作者EternallyLoveLyJoy
相关产品推荐
相关产品推荐

