我一直在努力寻找对以下代码中出现的错误的解释:
#include <stdlib.h>
int main() {
int m=65536;
int n=65536;
float *a;
a = (float *)malloc(m*n*sizeof(float));
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
a[i*n + j] = 0;
}
}
return 0;
}
为什么我会收到&#34;访问违规行为&#34;执行此程序时出错?
内存分配是成功的,问题出在一些迭代计数的嵌套for循环中。我尝试使用较小的m&amp; n值并且该程序有效。
这是否意味着我内存不足?
答案 0 :(得分:7)
问题是m*n*sizeof(float)
可能是溢出,导致价值相对较小。因此malloc
可以正常工作,但它没有像你期望的那样分配那么多的内存,因此你会跑掉缓冲区的末尾。
具体来说,如果int
是32位宽(这是常见的),则65336 * 65336
已经是溢出,因为您需要至少33位来表示它。 C ++中的有符号整数溢出(我相信C)会导致未定义的行为,但是一个常见的结果是最重要的位被删除,而你留下较低的位。在你的情况下,它给出0.那么然后乘以sizeof(float)
,但零乘以任何东西仍为零。
所以你试图分配0个字节。事实证明malloc会让你这样做,它会返回一个有效的指针,而不是一个空指针(如果分配失败,你会得到它)。 (参见下面的编辑。)
所以你有一个有效的指针,但取消引用它是无效的。您完全取消引用它的这一事实是实现的副作用:为了生成一个不会被重用的唯一地址,这就是当您要求0字节时要求malloc执行的操作,malloc可能分配了一个小但非零的字节数。当您尝试远远超出这些参考时,通常会遇到访问冲突。
修改强>
事实证明,当请求0字节时malloc执行的操作可能取决于您使用的是C还是C ++。在过去,C标准需要一个0字节的malloc来返回一个唯一的指针作为生成“特殊”指针值的方法。在现代C ++中,未定义0字节的malloc(参见C ++ 11标准的3.7.4.1节中的脚注35)。当我最初写答案时,我还没有意识到malloc的API已经以这种方式发生了变化。 (我喜欢它,当一个新手问题让我学习新的东西。)VC ++ 2013似乎保留了旧的行为(返回一个唯一的指针,分配0字节),即使在编译C ++时也是如此。
答案 1 :(得分:2)
你是2个问题的受害者。
首先是尺寸计算:
正如有些人指出的那样,你超出了size_t
的范围。您可以使用以下代码验证要分配的大小:
cout << "Max size_t is: " << SIZE_MAX<<endl;
cout << "Max int is : " << INT_MAX<<endl;
long long lsz = static_cast<long long>(m)*n*sizeof(float); // long long to see theoretical result
size_t sz = m*n*sizeof(float); // real result with overflow as will be used by malloc
cout << "Expected size: " << lsz << endl;
cout << "Requested size_t:" << sz << endl;
您会感到惊讶,但是对于MSVC13,由于溢出(!!),您要求0字节。您可能会使用不同的编译器获得另一个数字(导致低于预期的大小)。
其次,malloc()可能会返回一个问题指针:
对malloc()
的调用可能显示为成功,因为它不会返回nullptr
。分配的内存可能小于预期。甚至请求0字节也可能显示为成功,如文档here所示:如果size为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回指针不得解除引用。
float *a = reinterpret_cast<float*>(malloc(m*n*sizeof(float))); // prefer casts in future
if (a == nullptr)
cout << "Big trouble !"; // will not be called
替代方案
如果你绝对想要使用C,更喜欢calloc()
,你至少会得到一个空指针,因为该函数会注意到你有溢出:
float *b = reinterpret_cast<float*>(calloc(m,n*sizeof(float)));
但更好的方法将使用运算符 new [] :
float *c = new (std::nothrow) float[m*n]; // this is the C++ way to do it
if (c == nullptr)
cout << "new Big trouble !";
else {
cout << "\nnew Array: " << c << endl;
c[n*m-1] = 3.0; // check that last elements are accessible
}
<强> 编辑: 强>
它还受size_t
限制。
编辑2:
遇到问题时,new[]
会抛出bad_alloc
个例外,甚至bad_array_new_length
。如果你愿意,你可以尝试/捕捉这些。但如果您希望在内存不足时获得nullptr
,则必须使用Beat评论中指出的(std::nothrow)
。
对于你的情况,最佳方法,如果你真的需要这些大量的花车,那就是为了向量。因为它们也受size_t限制,但实际上你有2D数组,你可以使用向量向量(如果你有足够的内存):
vector <vector<float>> v (n, vector<float>(m));