Linux下x86_64架构C程序栈生长方向及地址分布疑问
为什么给a加正偏移就能访问到b的首字符
你混淆了字符串字面量的存储位置和指向字符串的指针变量的存储位置:
- C语言中双引号包裹的字符串字面量默认存储在只读数据段(
.rodata),这个区域属于进程的代码/数据映射区,和栈完全无关。 - 编译器会把程序内的所有字符串字面量按顺序(或优化后的连续布局)存放在
.rodata段,你示例中的"abcdefg"共占8字节(7个可见字符+末尾隐式的\0结束符),后面紧挨着存放的就是"mnopqrs",所以a+8刚好指向第二个字符串的首地址,和栈生长方向没有任何关系。 - 真正存放在栈上的是
a、b这两个指针变量本身,它们的地址对应输出里的&a、&b,和字符串本身的地址完全是两个概念。
0x55f开头和0x7ff开头的地址区间差异说明
- 0x55f开头的地址是可执行程序的映射区域:你的C程序编译后的代码段、只读数据段、全局可读写数据段都会被加载到这个低地址区间,字符串字面量就在这个区间的
.rodata段里。 - 0x7ff开头的地址是栈区域:x86_64 Linux系统默认会把栈映射在虚拟地址空间的高地址区域,所以局部变量(包括指针变量
a、b,long long变量c)的地址都在这个区间。 - 两个地址区间之间的是未被映射的空闲虚拟地址空间,不属于栈的空隙。中间通常还会分配堆空间(
malloc申请的内存就在这里)、动态链接库映射区、共享内存区等,未被使用的地址不会关联实际物理内存。
关于栈向下生长的补充说明
看你输出的局部变量地址:&a = 0x7ffc148d29b0、&b = 0x7ffc148d29b8、&c = 0x7ffc148d29c0,地址是递增的,这和栈向下生长的规则并不冲突:
- 栈向下生长指的是栈空间扩展的方向:当需要申请更多栈空间时(比如调用函数、新增局部变量),栈指针
rsp会向低地址方向移动。 - 你示例中三个变量的地址递增,是因为编译器在main函数的栈帧内,按从低地址到高地址的顺序分配了这三个局部变量的位置,后续如果再声明新的局部变量,新变量的地址会比
&a更低,符合栈向下生长的规则。
注意:你代码中通过
a+8访问b的字符串属于未定义行为,仅在当前编译环境下生效,换编译器、调整优化等级都可能出现不一样的结果,不要在生产代码中依赖这种布局。
内容的提问来源于stack exchange,提问作者Simon Rechermann
相关产品推荐
相关产品推荐

