我有这样一段代码在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个字节)。
注意:这不是功课的问题。 我是在栈是如何工作的好奇。 谢谢你的帮助。
Answer 1:
协议栈的行为(成长或成长向下)取决于应用程序二进制接口(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”被首先分配堆栈存储器。但是,通常的编译器堆栈存储器分配按照变量的声明的顺序)。
但在阵列的情况下,分配只具有单一指针和内存需要分配将由单个指针指向实际。 存储器需要是连续的对的阵列。 因此,尽管堆栈向下生长,为阵列栈长大。
Answer 2:
这实际上是两个问题。 一个是关于哪一种方式,当一个函数调用另一个堆栈增长 (当被分配一个新的帧),另一个是关于变量是如何在一个特定功能的框架布局。
无论是由C标准的规定,但答案是有一点不同:
- 哪种方式并不时被分配一个新的帧的堆栈增长-如果函数f()调用函数g(),将
f
的帧指针是大于或小于g
的帧指针? 这可以去任何一种方式-这取决于特定的编译器和体系结构(查找“调用约定”),但它始终是一个给定的平台内一致 (有一些例外怪异,见注释)。 向下是多见; 它是在86的情况下,的PowerPC,MIPS,SPARC,EE,和Cell的SPU。 - 如何一个函数的局部变量的堆栈帧里面布置? 这是不明确的,完全不可预测; 编译器可以自由安排其局部变量是什么样子,以获得最有效的结果。
Answer 3:
方向是堆叠成长的特定结构。 这就是说,我的理解是,只有极少数的硬件架构有长大栈。
一个堆栈增长的方向是独立的个体对象的布局。 因此,尽管堆叠可以生长下来,阵列不会(即&阵列[n]的永远是<&阵列[N + 1]);
Answer 4:
没有什么在责成事情是如何在栈上组织了所有的标准。 事实上,你可以建立一个符合标准的编译未在连续元素的数组元素存储在堆栈上可言,只要有智慧仍然做数组运算正确(所以它知道,例如,一个1是1K远离[0],并可能调整该)。
你可能会得到不同的结果的原因是因为,当堆叠可成长至“对象”添加到它,所述阵列是一个单一的“对象”,它可以具有在相反的顺序递增的数组元素。 但它不是安全地依赖于行为,因为方向可以改变和变量可以交换周围各种各样的原因,包括但不限于:
- 优化。
- 对准。
- 人的率性编译器的堆栈管理的一部分。
见这里为我的叠放方向上优秀论文:-)
在回答您的具体问题:
- 难道栈成长向上或向下?
不要紧,在所有(在标准而言),但是,既然你问,它可以增长向上或向下的内存,这取决于执行。 - A [2]和Q存储器地址之间发生什么事? 为什么有一个大的内存的区别在那里? (20个字节)?
不要紧,在所有(在标准而言)。 见上面的可能原因。
Answer 5:
在x86,堆栈帧的存储器“分配”仅含减去从栈指针字节必要数量(I相信其它架构是类似的)。 在这个意义上,我想堆growns“向下”,在地址获得您拨打更加深入到堆栈(但我总是想象的内存,当您移动在左上角从0开始,并取得更大的地址逐渐变小向右和向下缠绕,所以在我的心理图像堆长大......)。 变量的顺序被宣布可能不会对他们的地址有任何影响 - 我相信标准允许编译器重新排序,只要它不会引起副作用(有人请纠正我,如果我错了) 。 他们只是坚持到某处所创建的使用解决了差距,当它减去从堆栈指针的字节数。
阵列周围的间隙可以是某种类型的填充物,但它是神秘的给我。
Answer 6:
首先,它的8个字节的未用空间在存储器(它不是12时,请记住堆栈向下生长,所以这是不分配的空间是从604到597)。 为什么? 因为每个数据类型取入存储器空间的地址由它的尺寸整除开始。 在我们的情况的3个整数阵列需要12个字节的存储器空间和604是不整除12.因此,它离开空的空间,直到遇到一个存储器地址,该地址由12整除,它是596。
所以被分配到阵列的存储空间从596到584,但作为数组分配是在延续,数组所以第一个元素,从584个地址开始,而不是从596。
Answer 7:
向下生长,这是因为Little Endian字节序的标准,当涉及到设置在内存中的数据。
你可以看看它的一种方式是,如果你从顶部到底部和最大看看内存从0栈根本向上生长。
其原因为堆栈向下生长的是能够取消引用从堆栈或基指针的角度。
还记得从最低的任何类型增加到最高地址取消引用。 由于堆栈向下增长(从最高到最低的地址),这可以让你把堆栈像动态内存。
这就是为什么这么多的程序和脚本语言使用基于堆栈的虚拟机,而不是基于寄存器的。
Answer 8:
编译器可以自由地在当地的堆栈帧上的任何地方分配本地(自动)变量,你不能可靠地纯粹从推断堆栈生长方向。 你可以推断出堆栈比较嵌套堆栈帧的地址,即相对于它的被叫比较函数的栈帧中的局部变量的地址成长方向:
#include <stdio.h>
int f(int *x)
{
int a;
return x == NULL ? f(&a) : &a - x;
}
int main(void)
{
printf("stack grows %s!\n", f(NULL) < 0 ? "down" : "up");
return 0;
}
Answer 9:
我不认为这是确定的那样。 在一个阵列似乎“增长”,因为内存应该连续地分配。 然而,由于Q和S是不相关的都彼此,编译器只是坚持在堆栈中的任意空闲存储位置,他们每个人,可能是适合的整数大小最好的人。
什么A [2]和q之间的情况是,围绕Q的位置的空间是不足够大(即,不大于12字节)分配一个3整数数组。
Answer 10:
我的筹码似乎朝着较低编号地址扩展。
如果我使用不同的编译器调用它可能是不同的另一台计算机上,甚至是我自己的电脑上。 ...或编译器muigt选择不使用堆栈在所有(内嵌的一切(函数和变量,如果我没有把它们)的地址)。
$ cat stack.c
#include <stdio.h>
int stack(int x) {
printf("level %d: x is at %p\n", x, (void*)&x);
if (x == 0) return 0;
return stack(x - 1);
}
int main(void) {
stack(4);
return 0;
}
$ /usr/bin/gcc -Wall -Wextra -std=c89 -pedantic stack.c
$ ./a.out
level 4: x is at 0x7fff7781190c
level 3: x is at 0x7fff778118ec
level 2: x is at 0x7fff778118cc
level 1: x is at 0x7fff778118ac
level 0: x is at 0x7fff7781188c
Answer 11:
堆栈向下增长(在x86上)。 然而,堆栈中当一个块的功能负荷分配的,你没有保证什么样的顺序项目将在堆栈中。
在这种情况下,分配给两个整数和空间的栈上的三int数组。 它还分配的阵列后的额外12个字节,所以它看起来是这样的:
一个[12个字节]
填充(?)[12个字节]
S [4个字节]
Q [4个字节]
无论出于何种原因,你的编译器决定,它需要分配32个字节的这个功能,甚至更多。 这是不透明的,你作为一个C程序员,你不会知道为什么。
如果你想知道为什么,编译代码到汇编语言,我相信这是对-S和GCC / S对MS的C编译器。 如果你看一下在开幕说明该功能,你会看到老栈指针被保存,然后32(或别的东西!)正在从中减去。 从那里,你可以看到代码如何访问的内存,32字节的块,弄清你的编译器在做什么。 在函数结束时,你可以看到正在恢复堆栈指针。
Answer 12:
Answer 13:
堆栈不向下生长。 因此f(G(H())),分配给小时栈将开始以较低的地址,然后G和G的就会越低则f的。 但堆栈中的变量必须遵循C规格,
http://c0x.coding-guidelines.com/6.5.8.html
1206如果对象指向是相同的聚合对象的成员,指针结构中声明之后比较比指针先前在结构声明成员更大成员,指针数组元素与较大的下标值大于指针比较大至相同的元件阵列具有较低下标值。
&一个[0] <&A [1],必须始终是真实的,不管如何 'A' 被分配
Answer 14:
这取决于架构。 要检查自己的系统,使用此代码GeeksForGeeks :
// C program to check whether stack grows
// downward or upward.
#include<stdio.h>
void fun(int *main_local_addr)
{
int fun_local;
if (main_local_addr < &fun_local)
printf("Stack grows upward\n");
else
printf("Stack grows downward\n");
}
int main()
{
// fun's local variable
int main_local;
fun(&main_local);
return 0;
}
文章来源: Does stack grow upward or downward?