为什么当内存足够时malloc()会失败?

时间:2017-01-11 14:47:08

标签: c++ c memory malloc

我使用128GB内存的服务器进行一些计算。我需要malloc()一个大小为56120 * 56120的2D浮点数组。示例代码如下:

int main(int argc, char const *argv[])
{
    float *ls;
    int num = 56120,i,j;
    ls = (float *)malloc((num * num)*sizeof(float));
    if(ls == NULL){
        cout << "malloc failed !!!" << endl;
        while(1);
    }
    cout << "malloc succeeded ~~~" << endl;
    return 0;
}

代码编译成功,但是当我运行它时,它会显示"malloc failed !!!"。据我计算,只需要大约11GB的内存来容纳整个阵列。在我启动代码之前,我检查了服务器,并且有110GB的可用内存。为什么会发生错误?

我还发现,如果我将num减少到40000,那么malloc将会成功。

这是否意味着malloc()可以分配的最大内存有限制?

此外,如果我改变分配方式,直接声明这样大小的2D浮点数组,如下所示:

int main(int argc, char const *argv[])
{
    int num = 56120,i,j;
    float ls[3149454400];
    if(ls == NULL){
        cout << "malloc failed !!!" << endl;
        while(1);
    }
    cout << "malloc succeeded ~~~" << endl;
    for(i = num - 10 ; i < num; i ++){
        for( j = num - 10; j < num ; j++){
            ls[i*num + j] = 1;
        }
    }
    for(i = num - 11 ; i < num; i ++){
        for( j = num - 11; j < num ; j++){
            cout << ls[i*num + j] << endl;
        }
    }
    return 0;
}

然后我编译并运行它。我得到了"Segmentation fault"

我该如何解决这个问题?

5 个答案:

答案 0 :(得分:22)

问题是,你的计算

(num * num) * sizeof(float)

以32位有符号整数计算完成,num = 56120的结果为

-4582051584

然后将size_t解释为具有非常大的值

18446744069127500032

你没有那么多记忆;)这就是malloc()失败的原因。

在计算malloc时将num投射到size_t,然后它应该按预期工作。

答案 1 :(得分:8)

正如其他人所指出的,56120*56120在OP的平台上溢出int数学。这是未定义的行为(UB)。

malloc(size_t x)采用size_t参数,传递给它的值最好使用至少size_t数学计算。通过反转乘法顺序,这就完成了。 sizeof(float) * num会导致num在乘法之前扩展到至少size_t

int num = 56120,i,j;
// ls = (float *)malloc((num * num)*sizeof(float));
ls = (float *) malloc(sizeof(float) * num * num);

即使这会阻止UB,但这并不能防止数据sizeof(float)*56120*56120可能仍然超过SIZE_MAX的溢出。

代码可以预先检测到潜在的溢出。

if (num < 0 || SIZE_MAX/sizeof(float)/num < num) Handle_Error();

无需投射malloc()的结果 使用引用变量的大小比调整类型更容易编码和维护 当num == 0时,malloc(0) == NULL不一定是内存不足的 一起来:

int num = 56120;
if (num < 0 || ((num > 0) && SIZE_MAX/(sizeof *ls)/num < num)) {
  Handle_Error();
}
ls = malloc(sizeof *ls * num * num);
if (ls == NULL && num != 0) {
  Handle_OOM();
}

答案 2 :(得分:7)

int num = 56120,i,j;
ls = (float *)malloc((num * num)*sizeof(float));

num * num56120*561203149454400溢出signed int,导致未定义的行为。

40000工作的原因是40000 * 40000可以表示为int。

num的类型更改为long long(或甚至unsigned int

答案 3 :(得分:6)

这与其他人写的不同,但对我来说,将变量num更改为size_t可以进行分配。可能是num * num溢出了malloc的int。使用56120 * 56120而不是num * num执行malloc应该抛出溢出错误。

答案 4 :(得分:2)

float ls[3149454400];是一个具有自动存储类型的数组,通常在进程堆栈上分配。默认情况下,进程堆栈受限于的值,小于您试图在那里推送的12GB。因此,您观察到的分段错误是由堆栈溢出引起的,而不是由malloc引起的。