繁体   English   中英

当有足够的内存时,为什么malloc()失败?

[英]Why does malloc() fail when there is enough memory?

我正在使用具有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"

我该如何解决?

问题是,您的计算

(num * num) * sizeof(float)

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

-4582051584

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

18446744069127500032

您没有太多的内存;)这就是malloc()失败的原因。

在计算malloc时将num强制转换为size_t ,然后它将按预期工作。

正如其他人指出的那样, 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 == 0malloc(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();
}
int num = 56120,i,j;
ls = (float *)malloc((num * num)*sizeof(float));

num * num56120*56120 ,它是3149454400 ,它溢出有signed int ,从而导致未定义的行为。

40000有效的原因是40000 * 40000可表示为一个int。

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

这与其他人所写的相反,但对我来说,将变量num从int更改为size_t允许分配。 可能是num * num溢出了malloc的int。 用56120 * 56120而不是num * num进行malloc应该会引发溢出错误。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM