我在c中有这段代码:
int q = 10; int s = 5; int a[3]; printf("Address of a: %d\n", (int)a); printf("Address of a[1]: %d\n", (int)&a[1]); printf("Address of a[2]: %d\n", (int)&a[2]); printf("Address of q: %d\n", (int)&q); printf("Address of s: %d\n", (int)&s);
输出是:
Address of a: 2293584 Address of a[1]: 2293588 Address of a[2]: 2293592 Address of q: 2293612 Address of s: 2293608
所以,我看到,从那里a
开始a[2]
,内存地址每个增加4个字节.但是,从q
到s
,内存地址减少了4个字节.
我想知道两件事:
堆栈是成长还是下降?(在这种情况下看起来对我来说都是)
a[2]
和q
内存地址之间发生了什么?为什么那里存在很大的记忆差异?(20个字节).
注意:这不是作业问题.我很好奇堆栈是如何工作的.谢谢你的帮助.
堆栈(成长或增长)的行为取决于应用程序二进制接口(ABI)以及如何组织调用堆栈(也称为激活记录).
在其整个生命周期中,程序必然会与其他程序(如OS)进行通信.ABI确定程序如何与另一个程序通信.
不同体系结构的堆栈可以以任何一种方式增长,但对于体系结构,它将是一致的.请检查此维基链接.但是,堆栈的增长是由该架构的ABI决定的.
例如,如果您使用MIPS ABI,则调用堆栈定义如下.
让我们考虑函数'fn1'调用'fn2'.现在'fn2'看到的堆栈帧如下:
direction of | | growth of +---------------------------------+ stack | Parameters passed by fn1(caller)| from higher addr.| | to lower addr. | Direction of growth is opposite | | | to direction of stack growth | | +---------------------------------+ <-- SP on entry to fn2 | | Return address from fn2(callee) | V +---------------------------------+ | Callee saved registers being | | used in the callee function | +---------------------------------+ | Local variables of fn2 | |(Direction of growth of frame is | | same as direction of growth of | | stack) | +---------------------------------+ | Arguments to functions called | | by fn2 | +---------------------------------+ <- Current SP after stack frame is allocated
现在你可以看到堆栈向下增长.因此,如果变量被分配给函数的本地帧,则变量的地址实际上向下增长.编译器可以决定内存分配的变量顺序.(在你的情况下,它可以是'q'或's',它是第一次分配的堆栈内存.但是,通常编译器会根据变量声明的顺序堆栈内存分配).
但是在数组的情况下,分配只有单个指针,而需要分配的内存实际上是由单个指针指向的.内存需要与数组连续.因此,尽管堆栈向下增长,但对于数组,堆栈会增长.
这实际上是两个问题.一个是关于当一个函数调用另一个函数时(当分配新帧时)堆栈增长的方式,另一个是关于如何在特定函数的框架中布置变量.
C标准都没有规定,但答案有点不同:
当分配新帧时,堆栈会以哪种方式增长 - 如果函数f()调用函数g(),那么f
帧指针是否大于或小于g
帧的指针? 这可以采用任何一种方式 - 它取决于特定的编译器和体系结构(查找"调用约定"),但它在给定平台内始终保持一致(有一些奇怪的例外,请参阅注释).向下是更常见的; 在x86,PowerPC,MIPS,SPARC,EE和Cell SPU中就是这种情况.
函数的局部变量如何在其堆栈框架内布局?这是未指明的,完全不可预测; 编译器可以自由安排其局部变量,但是它喜欢获得最有效的结果.
堆栈增长的方向是体系结构特定的.也就是说,我的理解是只有极少数硬件架构具有成长的堆栈.
堆栈增长的方向与单个对象的布局无关.因此,当堆栈可能长大时,数组将不会(即&array [n]将始终为<&array [n + 1]);