我正在运行以下代码,发现一些我不理解的东西:
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
在这里我们可以清楚地看到,字符串以其起始地址按值的升序存储,但指针变量的地址按值的降序-内存中存储。
我只是想知道为什么会这样? 指针变量的地址也不应该按升序排列吗?
答案 0 :(得分:8)
存储在s1
,s2
和s3
中的地址的数值(即,初始化为"hi"
,{{ 1}}和"Bye"
)保证不相等,并且它们可能彼此靠近,但是不要求它们相邻或以任何特定顺序排列。同样,地址"Out"
,&s1
和&s2
的数值(即局部变量&s3
,s1
和{ {1}})保证不相等,并且可能彼此靠近,但不要求相邻或以任何特定顺序存在。
s2
,s3
和s1
保存字符串文字的地址。字符串文字是具有静态存储持续时间的常量数组的语法糖,这意味着它们将在程序开始运行之前进行分配和初始化。由于它们不是具有静态存储持续时间的相同数组,因此 1 它们不能具有相同的地址,但除此之外,从技术上讲,它们可以位于RAM中的任何位置。在实践中,它们将被放置在RAM区域中,以保留只读数据,这意味着它们将彼此靠近,但是编译器和链接器在方便他们的地方对该区域进行了布局,因此您不应该这样做依靠他们的方式。
s2
,s3
和&s1
是局部变量的地址。再一次,由于它们不是全部相同的变量,因此它们不能具有相同的地址,而且由于它们都属于同一个函数,因此它们很可能彼此接近,但又一次,它们的地址的确切关系内存由编译器决定,您不应该依赖它。熟悉汇编语言的人们经常认为局部变量会一次被推送到硬件堆栈中,因此它们应该根据“堆栈增长的方向” 2 连续,但是要编译代码不会那样做。对于编译器来说,将堆栈指针一次移到每个函数上然后将其保留在那里,创建一个称为“堆栈框架”的东西更为方便。这意味着局部变量可以立即有效地全部存在,并且它们在堆栈框架中的地址可以是编译器最方便的。 (例如,可以按大小对它们进行排序,以使最小的变量最靠近堆栈指针,并且可以以较小的位移来访问。)
1 C标准中有一种特殊情况,它允许将字符串“去重复”,即,如果您在一个函数的顶部写上&s2
而在&s3
上写在另一个顶部,存储在const char *a = "foo";
和const char *b = "foo";
中的地址可能会相同。但是您的字符串并不相同,因此无法以这种方式对待它们。
2 有趣的事实:C实现完全不需要具有硬件堆栈,更不用说向特定方向发展的了!它需要支持递归函数调用,但是完全没有指定如何实现。
答案 1 :(得分:4)
字符串存储在程序的R/O - Data section
中。 它们可以按照编译器选择的任何顺序存储。局部变量存储在堆栈中,而它们也可以以任何顺序存储,通常堆栈在内存中变小而堆在内存中变长。