栈内存是在运行时还是编译时分配的?

14

堆栈是在运行时还是编译时分配的?
示例:

void main()
{
    int x;
    scanf("%d", &x);
    int arr[x];
}

你的目标平台是什么? - user957902
3
递归的存在告诉我,在运行时必须分配堆栈。 - Blastfurnace
@user957902,目标平台在这里有什么影响吗? - Jay
2
一些C的实现,例如针对低端Pic微控制器的C,没有堆栈。C是一种高级语言,它被编译成什么很重要。这将取决于平台。 - user957902
@Oli:没错,但我认为提问者对编译器和运行时的心理模型缺少重要的东西。提问者的例子考虑了一个从stdin读取大小的VLA。我不知道是什么混淆了思想,导致有人想知道是否可以在编译时分配该VLA,可能是在几年之前就已知其大小。因此,这是我的经验法则。 - Steve Jessop
显示剩余3条评论
8个回答

11

栈在运行时分配;每个栈帧的布局在编译时决定,除了变量大小数组。


仅供文档使用,这个链接解释了“堆栈帧”一词的含义。 - Cacho Santa
“每个堆栈帧的布局”究竟是什么意思?我不明白“布局”这个术语……无论如何,整个堆栈大小并非在编译时确定,对吗? - starriet
1
编译器决定函数内每个局部变量相对于堆栈指针的偏移量,以及传递到函数中的每个参数的偏移量。 - Sergey Kalinichenko
1
@starriet 不是虚拟地址,而是相对于堆栈上其他变量的位置。例如,如果您的函数有两个int参数x和y,则编译器可能会给x一个偏移量为0,给y一个偏移量为4。当堆栈指针位于0xC000时调用您的函数,x将占据字节0xC000..0xC003,而y将占据0xC004..0xC007。当堆栈指针位于0xF450时调用您的函数,x将位于0xF450..0xF453,而y将位于0xF454..0xF457。 - Sergey Kalinichenko
1
@starriet 是的,那是正确的。请注意,在同一次运行中,栈指针可能会不同,这取决于调用所在函数的路径。对此进行很好的说明的一个例子是递归函数的调用:每次调用都会有一个新的栈指针,因此变量在不同的调用中被保持分开,但相对于相同调用内部的变量布局保持不变,因为这是在编译时确定的。 - Sergey Kalinichenko
显示剩余3条评论

4

它必须在运行时分配。考虑以下内容:

void a( void )
{
    int x;
}

void b( void )
{
    int y;
    a();
}

int main( void )
{
    a();
    b();
}

a()函数中的栈内局部变量x的地址在两次调用之间会有所不同。正如blinkenlights所指出的,每个函数栈帧的布局在很大程度上是在编译时确定的,但该栈帧的放置位置则是在运行时确定的。


谢谢您提供了一个很好的例子。但我仍然不确定在编译时确定了什么。您能否请澄清一下每个函数堆栈帧的“布局”具体是什么? - starriet
1
每个本地变量的地址,相对于堆栈帧的起始位置。相对位置通常在编译时确定。 - Russell Borogove

2

如果我在我的机器上编译代码,但在你的机器上执行它,你会如何分配编译时间?编译器如何能够预先为你的机器上的堆栈预留内存?


1
我猜这取决于你对“分配”的定义。例如,程序硬编码为使用虚拟地址空间的特定区域肯定是可能的。 - Oliver Charlesworth
@OliCharlesworth,你如何确保编译器能够确保该区域实际上适合内存?我曾经在一个以字节计算内存的MCU上工作过,而我在工作中使用的机器有多个G级别。在那些机器上可以容纳的东西,在提到的MCU上可能就没有那么大的机会了。 - Rune FS
1
需要确保它适合吗?使用运行时分配,通常也不会确保有足够的内存,如果堆栈耗尽内存,程序将会崩溃。也就是所谓的堆栈溢出。 - nos
1
@RuneFS:我并不是说总是可能的,只是说它不是从来不可能的。 - Oliver Charlesworth

2

这篇文章应该能帮到你。堆栈内存是在运行时分配的。

需要记住的是,它必须在运行时分配,因为编译器无法知道一个函数被调用了多少次,或者while循环执行了多少次等。


2
为了补充其他答案(一般情况下是正确的),理论上有时可以在编译时分配堆栈空间(取决于您对“分配”定义的方式)。
具体地说,如果您的程序没有函数指针或递归,则可以使用静态分析来确定所需的最大堆栈大小。事实上,一些嵌入式编译器正是这样做的。

1

0
当然,栈是在运行时分配的。执行代码需要栈内存。
请查看这个链接,它讨论了C程序的内存布局。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接