C语言 为什么连续声明的指针变量按其地址的降序存储在内存中?



我正在运行以下代码并发现了一些我不明白的东西:

main(){
char *s1 = "hi";
char *s2 = "Bye";
char *s3 = "Out";
printf("s %p | p %p : s %p | p %p : s %p | p %p", s1, &s1, s2, &s2, s3, &s3); 
}

输出结果为:

s 0000000000409020 | p 000000000064FE58 : s 0000000000409023 | p 000000000064FE50 : s 0000000000409027 | p 000000000064FE48

在这里,我们可以清楚地看到字符串与其起始地址一起按值的递增顺序存储,但指针变量的地址按值的递减顺序存储 - 在内存中。

我只是好奇为什么会这样? 指针变量的地址不应该也按递增顺序排列吗?

存储在s1s2s3中的地址的数值(即初始化为"hi""Bye""Out"的字符数组的地址(保证不相等,并且它们可能彼此接近,但不要求它们相邻或按任何特定顺序排列。 类似地,地址&s1&s2&s3(即局部变量s1s2s3的地址(的数值保证不相等,并且可能彼此接近,但不需要相邻或按任何特定顺序排列。

s1s2s3保存字符串文字的地址。 字符串文字是具有静态存储持续时间的常量数组的语法糖,这意味着它们都是在程序开始运行之前分配和初始化的。 由于它们不是具有静态存储持续时间的同一阵列,1因此它们不能具有相同的地址,但除此之外,从技术上讲,它们可以位于 RAM 中的任何位置。 实际上,它们将被放入为只读数据预留的RAM区域中,这意味着它们将彼此靠近,但是编译器和链接器会为了方便它们而布置该区域,您不应该依赖它们是如何做到的。

&s1&s2&s3是局部变量的地址。 同样,由于它们不都是同一个变量,因此它们不能具有相同的地址,并且再次,它们可能彼此接近,因为它们都属于同一个函数,但同样,它们在内存中的地址的确切关系取决于编译器,您不应该依赖它。 熟悉汇编语言的人通常认为局部变量会一次推送一个到硬件堆栈上,因此它们应该根据"堆栈增长的方向">2连续,但编译代码不会这样做。 编译器更方便的是,在进入每个函数时移动堆栈指针一次,然后将其保留在那里,从而创建称为"堆栈帧"的东西。 这意味着局部变量实际上同时出现,它们在堆栈帧中的地址可以是编译器最方便的任何地址。 (例如,它们可以按大小排序,以便最小的变量最接近堆栈指针,并且可以以较短的位移访问。


1C 标准中有一个特殊情况,它允许字符串被"重复数据删除",即如果你在一个函数的顶部写const char *a = "foo";,而在另一个函数的顶部写const char *b = "foo";,存储在ab中的地址最终可能会相同。 但是您的字符串并不相同,因此不能以这种方式处理它们。

2有趣的事实:C 实现根本不需要硬件堆栈,更不用说向特定方向增长的堆栈了! 它需要支持递归函数调用,但它如何做到这一点是完全没有指定的。

字符串存储在程序的R/O - Data section中。它们可以按编译器选择的任何顺序存储。 局部变量存储在堆栈上,虽然这些变量也可以按任何顺序存储,但堆栈在内存中增长,而堆在内存中增长。

最新更新