我只是想确保我做对了。 想象一下,我机器上任何程序的堆栈大小都是 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()得到它们的堆栈“实例”?
答案 0 :(得分:6)
虽然其他人都戴着他们的帽子,关于“是的有帮助”,“不会不会”,“没有任何区别”,等等。我会告诉你,你所要求的是如此依赖于实施它是不可能得到一个直接的普遍答案。您描述的变量是自动,这意味着它们的可见性和定义受其功能范围的限制。幕后发生的事情是完全平台依赖。
我已经提到过一种情况,一个实现可能潜在地都有两个函数自动变量占用内存,即完全优化调用,因此需要足够的占用内存专用于自动变量来拥有它< em> all 在一个地方。 你不知道老实说你最好不要假设你需要,除非你编写嵌入式,内核或驱动程序代码,其中每个字节都很重要,平台已成定局。
C标准非常具体在 not 中处理这样的事情,将其留给要实现的实现。搜索C99标准,你会发现单词“stack”正好零次。这不是偶然的。
那就是说,如果你自己设计一个简单的实现 ,那么考虑你所关心的东西在设计中是最重要的,但除非你要失败那条道路(或你已经知道平台答案的内核/驱动程序/嵌入式街道),根本就没有具体方式说“它以这种方式工作......”你的实现可能就像你描述的那样,在自助晚餐的众所周知的板堆。但这绝不能用于推断所有(甚至大多数)实现都会做同样的事情。只要他们遵守有关自动变量的标准所提出的规则,其余部分就是细节中的魔鬼。
答案 1 :(得分:5)
PPS。换句话说,就是这种情况,每次,每个函数f(),f1()和f2()得到它们的堆栈“实例”?
调用函数时,它将在调用期间分配内存。函数返回时,返回内存。
在您的示例中,f2()
将使用与f1()
相同的堆栈实例,但由于f1()
在调用f2()
时返回,因此堆栈内存使用f1()
可以免费重复使用f2()
。
堆栈是按线程分配的,因此在多线程程序中,每个线程都有自己的堆栈。
修改强>
应该指出的是,上面的描述是编译器通常如何实现局部变量。 C标准没有规定需要使用堆栈,但对于未知数量的进程/线程需要共享有限内存的环境,它是最好的解决方案(到目前为止)。
修改2
将f
划分为f1
和f2
是减少堆栈内存消耗的一种方法。其他方法是在堆上分配内存(使用malloc / free)或静态分配(静态分配不是线程安全的,因此它会降低可移植性和可重用性,并且只应作为最后的手段使用)。
假设我在f()中定义了一个局部变量,当程序在f1()中输入时会发生什么?
它仍然存在。您可以将堆栈视为一堆文件。无论何时调用函数,调用者都会添加一张纸,其中包含返回值的返回地址和空格。然后被调用的函数添加另一篇带有局部变量的论文。 当函数返回时,它会删除顶部纸张(带有局部变量)并在下一张纸上记下返回值。调用代码读取返回值并删除顶部纸张,再次使用调用者的局部变量显示纸张。
答案 2 :(得分:1)
是分割函数将节省堆栈空间,因为当函数完成执行时(执行该函数中的最后一个可执行语句)并返回它会清除它使用的堆栈内存。
在函数f1()完成执行后,没有数据(存储在char x [300] char [300]中)
答案 3 :(得分:0)
是的,当编译器执行程序时,它占用或需要内存所需的当前上下文。因此,在第一种情况下,当您的函数需要的空间超过可用空间时,它将抛出异常。
虽然在第二种情况下首先尝试运行main函数(理想情况下)但它不需要内存。
所以当它试图运行f1()时,需要的内存分配不大于可用内存,这样才能成功执行。
从函数f1()返回的
将被解除分配并且你的完整(理想情况下)现在可用f2()开始运行再次需要内存。
所以你的程序将成功执行。