当编写C ++代码,我已经学会了使用堆栈来存储内存是一个好主意。
但最近我遇到了一个问题:
我有一个实验,包括那个看起来像这样的代码:
void fun(const unsigned int N) {
float data_1[N*N];
float data_2[N*N];
/* Do magic */
}
该代码exploted与seqmentation故障随机的,而且我不知道为什么。
原来,问题是,我是想保存的东西,是要大上我的筹码,有没有检测这个办法? 或至少检测到它出了什么问题?
float data_1[N*N];
float data_2[N*N];
这些是可变长度数组(VLA),作为N
不是一个常量表达式。 的常量性的参数仅确保N
是只读的。 它不会告诉编译器N
是常量表达式。
沃拉斯被允许在只有C99; 在其他版本的C和C的所有版本++他们不允许的。 然而,一些编译器提供VLA如编译器的扩展功能。 如果你正在用gcc编译,然后尝试使用-pedantic
选项,它会告诉你,这是不允许的。
现在为什么你的程序给段错误,可能是因为堆栈溢出由于大的值N * N
:
考虑使用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
}
这是非常难以检测堆栈已满,而不是在所有便携。 其中一个最大的问题是,堆栈帧大小可变的(特别是使用变长数组,这真的只是在做什么人之前做的更标准的方式,当alloca()
所以你不能用简单的代理像堆栈帧的数目。
之一即大多便携式最简单的方法是把一个变量(可能类型的char
,以便一个指向它是一个char*
)以已知的深度在栈上,并然后测量从该点的距离的变量(的通过简单的指针运算在当前堆栈帧是同一类型)。 添加在你多大的空间即将分配的估计,你可以有一个很好的猜测,还判定是否堆栈要炸掉你。 这样做的问题是,你不知道,堆在不断增长(不,他们不都生长在同一个方向!),并制定出栈空间的大小,方向本身是相当混乱的(你可以尝试之类的系统限制,但他们真的很别扭)。 加上黑客系数是非常高的。
我已经看到了在32位Windows中使用的其它伎俩只能是尝试alloca()
有足够的空间和处理,如果有足够的房间,将发生系统异常。
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;
}
此代码是非常不便携,与旧GCC建筑(例如,不要做这个工作,在64位Windows计算)需要一些讨厌的内联汇编程序,而不是! 结构化异常处理(这这是一个使用的)是当中最黑的Windows黑色艺术。 (不要return
从里面__try
结构。)
尝试使用替代功能,如malloc的。 这将返回null明确,如果未能找到您所请求的大小的内存块。
当然,在这种情况下,不要忘记在函数结束时释放此内存,在完成后。
此外,你可以检查你的编译器的设置,用什么堆栈内存限制它生成的二进制文件。
其中原因的人说,这是更好地使用堆栈,而不是堆内存可能是因为一个事实,即在堆栈的顶部分配的变量,当你离开函数的身体会自动弹出。 用于存储信息的大的块中,通常使用堆存储器和其他数据结构等链表或树木。 另外在栈上分配的记忆是有限的,更低于你可以在堆空间分配。 我觉得这是更好地管理内存的分配和更仔细地释放试图利用堆栈用于存储大数据代替。
您可以使用框架,管理你的内存分配。 也可以使用VDL检查未释放你的内存泄漏和回忆。
是存在的检测该的方法吗?
没有,一般。
堆栈大小是平台depedent。 典型地, 操作系统决定堆栈的大小。 所以,你可以检查你的OS( ulimit -s
在Linux上),看看它有多少堆栈存储器分配你的程序。
如果你的编译器支持stackavail()
那么你可以检查它。 这是更好地去堆分配的内存在你不确定是否会超出堆栈限制的情况。