堆栈内存是如何组织的?

时间:2014-02-05 11:04:09

标签: c

我只是想确保我做对了。 想象一下,我机器上任何程序的堆栈大小都是 800字节(仅举例)。 然后,代码如下:

void f()
{
char x[300];
char y[300];
char z[300];
char t[300];
}

应该溢出堆栈权利(因为1200> 800)?

现在我的问题是,是以下方法 一个很好的办法来打败上面提到的堆栈溢出问题?

void f()
{
   f1();
   f2();
}

其中:

void f1()
{
    char x[300];
    char y[300];
}

void f2()
{
    char z[300];
    char t[300];
}

这种方式根据我的推理,每个函数只消耗600个字节(&<800) 堆栈内存,所以一切都应该没问题。我是对的吗?

PS。为了这个例子,我不是指可能占用堆栈空间的“其他”数据。

PPS。换句话说,就是这种情况,每次,每个函数f(),f1()和f2()得到它们的堆栈“实例”?

4 个答案:

答案 0 :(得分:6)

虽然其他人都戴着他们的帽子,关于“是的有帮助”,“不会不会”,“没有任何区别”,等等。我会告诉你,你所要求的是如此依赖于实施它是不可能得到一个直接的普遍答案。您描述的变量是自动,这意味着它们的可见性定义受其功能范围的限制。幕后发生的事情是完全平台依赖。

我已经提到过一种情况,一个实现可能潜在地都有两个函数自动变量占用内存,即完全优化调用,因此需要足够的占用内存专用于自动变量来拥有它< em> all 在一个地方。 你不知道老实说你最好不要假设你需要,除非你编写嵌入式,内核或驱动程序代码,其中每个字节都很重要,平台已成定局。

C标准非常具体在 not 中处理这样的事情,将其留给要实现的实现。搜索C99标准,你会发现单词“stack”正好次。这不是偶然的。

那就是说,如果你自己设计一个简单的实现 ,那么考虑你所关心的东西在设计中是最重要的,但除非你要失败那条道路(或你已经知道平台答案的内核/驱动程序/嵌入式街道),根本就没有具体方式说“它以这种方式工作......”你的实现可能就像你描述的那样,在自助晚餐的众所周知的板堆。但这绝不能用于推断所有(甚至大多数)实现都会做同样的事情。只要他们遵守有关自动变量的标准所提出的规则,其余部分就是细节中的魔鬼。

答案 1 :(得分:5)

  

PPS。换句话说,就是这种情况,每次,每个函数f(),f1()和f2()得到它们的堆栈“实例”?

调用函数时,它将在调用期间分配内存。函数返回时,返回内存。

在您的示例中,f2()将使用与f1()相同的堆栈实例,但由于f1()在调用f2()时返回,因此堆栈内存使用f1()可以免费重复使用f2()

堆栈是按线程分配的,因此在多线程程序中,每个线程都有自己的堆栈。

修改

应该指出的是,上面的描述是编译器通常如何实现局部变量。 C标准没有规定需要使用堆栈,但对于未知数量的进程/线程需要共享有限内存的环境,它是最好的解决方案(到目前为止)。

修改2

f划分为f1f2是减少堆栈内存消耗的一种方法。其他方法是在堆上分配内存(使用malloc / free)或静态分配(静态分配不是线程安全的,因此它会降低可移植性和可重用性,并且只应作为最后的手段使用)。

  

假设我在f()中定义了一个局部变量,当程序在f1()中输入时会发生什么?

它仍然存在。您可以将堆栈视为一堆文件。无论何时调用函数,调用者都会添加一张纸,其中包含返回值的返回地址和空格。然后被调用的函数添加另一篇带有局部变量的论文。 当函数返回时,它会删除顶部纸张(带有局部变量)并在下一张纸上记下返回值。调用代码读取返回值并删除顶部纸张,再次使用调用者的局部变量显示纸张。

答案 2 :(得分:1)

是分割函数将节省堆栈空间,因为当函数完成执行时(执行该函数中的最后一个可执行语句)并返回它会清除它使用的堆栈内存。

在函数f1()完成执行后,没有数据(存储在char x [300] char [300]中)

答案 3 :(得分:0)

是的,当编译器执行程序时,它占用或需要内存所需的当前上下文。因此,在第一种情况下,当您的函数需要的空间超过可用空间时,它将抛出异常。

虽然在第二种情况下首先尝试运行main函数(理想情况下)但它不需要内存。

  

main

所以当它试图运行f1()时,需要的内存分配不大于可用内存,这样才能成功执行。

  

f1

从函数f1()返回的

将被解除分配并且你的完整(理想情况下)现在可用f2()开始运行再次需要内存。

  

f2

所以你的程序将成功执行。