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

时间:2018-09-17 19:13:33

标签: c pointers

我正在运行以下代码,发现一些我不理解的东西:

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

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

我只是想知道为什么会这样? 指针变量的地址也不应该按升序排列吗?

2 个答案:

答案 0 :(得分:8)

存储在s1s2s3中的地址的数值(即,初始化为"hi",{{ 1}}和"Bye")保证不相等,并且它们可能彼此靠近,但是不要求它们相邻或以任何特定顺序排列。同样,地址"Out"&s1&s2的数值(即局部变量&s3s1和{ {1}})保证不相等,并且可能彼此靠近,但不要求相邻或以任何特定顺序存在。

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

s2s3&s1是局部变量的地址。再一次,由于它们不是全部相同的变量,因此它们不能具有相同的地址,而且由于它们都属于同一个函数,因此它们很可能彼此接近,但又一次,它们的地址的确切关系内存由编译器决定,您不应该依赖它。熟悉汇编语言的人们经常认为局部变量会一次被推送到硬件堆栈中,因此它们应该根据“堆栈增长的方向” 2 连续,但是要编译代码不会那样做。对于编译器来说,将堆栈指针一次移到每个函数上然后将其保留在那里,创建一个称为“堆栈框架”的东西更为方便。这意味着局部变量可以立即有效地全部存在,并且它们在堆栈框架中的地址可以是编译器最方便的。 (例如,可以按大小对它们进行排序,以使最小的变量最靠近堆栈指针,并且可以以较小的位移来访问。)


1 C标准中有一种特殊情况,它允许将字符串“去重复”,即,如果您在一个函数的顶部写上&s2而在&s3上写在另一个顶部,存储在const char *a = "foo";const char *b = "foo";中的地址可能会相同。但是您的字符串并不相同,因此无法以这种方式对待它们。

2 有趣的事实:C实现完全不需要具有硬件堆栈,更不用说向特定方向发展的了!它需要支持递归函数调用,但是完全没有指定如何实现。

答案 1 :(得分:4)

字符串存储在程序的R/O - Data section中。 它们可以按照编译器选择的任何顺序存储。局部变量存储在堆栈中,而它们也可以以任何顺序存储,通常堆栈在内存中变小而堆在内存中变长。