编写C ++代码时,我了解到使用堆栈存储内存是一个好主意。
但最近我遇到了一个问题:
我有一个代码看起来像这样的实验:
void fun(const unsigned int N) {
float data_1[N*N];
float data_2[N*N];
/* Do magic */
}
代码随机抽取了一个seqmentation故障,我不明白为什么。
事实证明,问题在于我试图在我的堆栈中存储大的东西,有没有办法检测到这个?或者至少发现它出了问题?
答案 0 :(得分:5)
float data_1[N*N];
float data_2[N*N];
这些是可变长度数组(VLA),因为N
不是常量表达式。参数中的 const-ness 仅确保N
是只读的。它没有告诉编译器N
是常量表达式。
仅允许在C99中使用VLA;在其他版本的C和所有版本的C ++中,它们是不允许的。但是,一些编译器提供VLA作为编译器扩展功能。如果您正在使用GCC进行编译,请尝试使用-pedantic
选项,它会告诉您不允许这样做。
现在为什么你的程序会给出段错误,可能是因为N * N
的{{1}}值较大而导致stack-overflow:
考虑将std::vector
用作:
#include <vector>
void fun(const unsigned int N)
{
std::vector<float> data_1(N*N);
std::vector<float> data_2(N*N);
//your code
}
答案 1 :(得分:2)
很难检测到堆栈已满,而且根本不是便携式。最大的问题之一是堆栈帧的大小可变(特别是在使用可变长度数组时,这实际上只是使用alloca()
执行人们之前所做事情的一种更标准的方式)因此您无法使用简单的代理,比如堆栈帧的数量。
可移植的最简单方法之一是在已知的位置放置一个变量(可能是char
类型,以便指向它的指针是char*
)堆栈上的深度,然后通过简单的指针算法测量当前堆栈帧中从该点到变量(相同类型)的距离。加上你要分配多少空间的估计,你可以很好地猜测堆栈是否会炸毁你。这个问题是你不知道堆栈的增长方向(不,它们并不都是朝着同一个方向增长!)并且计算堆栈空间的大小本身就相当混乱(你可以尝试类似系统限制的东西,但它们真的很尴尬)。加上黑客因素非常高。
我在32位Windows上使用的另一个技巧是尝试alloca()
足够的空间并处理在没有足够空间时可能发生的系统异常。 / p>
int have_enough_stack_space(void) {
int enough_space = 0;
__try { /* Yes, that's got a double-underscore. */
alloca(SOME_VALUE_THAT_MEANS_ENOUGH_SPACE);
enough_space = 1;
} __except (EXCEPTION_EXECUTE_HANDLER) {}
return enough_space;
}
此代码非常不可移植(例如,不要指望它在64位Windows上工作),并且使用较旧的gcc构建需要一些讨厌的内联汇编程序!结构化异常处理(这是一种用途)是Windows上最黑的艺术之一。 (并且不要return
构造内的__try
。)
答案 2 :(得分:1)
尝试使用像malloc这样的函数。如果找不到您请求的大小的内存块,它将显式返回null。
当然,在这种情况下,完成后不要忘记在功能结束时释放这个内存。
此外,您可以检查编译器的设置,以及它生成二进制文件的堆栈内存限制。
答案 3 :(得分:1)
人们说使用堆栈而不是堆内存更好的原因之一可能是因为当你离开函数体时,分配在堆栈顶部的变量会自动弹出。为了存储大块信息,通常使用堆内存和其他数据结构,如链表或树。在堆栈上分配的内存也是有限的,远远少于在堆空间中分配的内存。我认为最好是管理内存分配并更谨慎地发布,而不是尝试使用堆栈来存储大数据。
您可以使用管理内存分配的框架。您也可以使用VDL检查内存泄漏和未发布的内存。
答案 4 :(得分:1)
有没有办法检测到这个?
不,一般而言。
堆栈大小取决于平台。通常,操作系统决定堆栈的大小。因此,您可以检查您的操作系统(Linux上的ulimit -s
),看看它为您的程序分配了多少堆栈内存。
如果您的编译器支持stackavail()
,那么您可以检查它。在您不确定是否超出堆栈限制的情况下,最好使用堆分配的内存。