Почему malloc() терпит неудачу, когда имеется достаточно памяти?

Я использую сервер с памятью 128 ГБ для выполнения некоторых вычислений. Мне нужно malloc() массив 2D float размером 56120 * 56120. Пример кода выглядит следующим образом:

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 !!!". Как я рассчитывал, для хранения всего массива требуется всего 11 ГБ памяти. Прежде чем я начал код, я проверил сервер, и было доступно 110 ГБ свободной памяти. Почему происходит ошибка?

Я также обнаружил, что если я уменьшу 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".

Как я могу это решить?

Ответ 1

Проблема заключается в том, что ваш расчет

(num * num) * sizeof(float)

выполняется как 32-разрядное целочисленное вычисление со знаком и результат для num = 56120 равен

-4582051584

который затем интерпретируется для size_t с очень большим значением

18446744069127500032

У вас не так много памяти;) Вот почему malloc() терпит неудачу.

Откажитесь от num до size_t при вычислении malloc, тогда он должен работать как ожидалось.

Ответ 2

Как указывали другие, 56120*56120 переполняет int математику на платформе OP. Это поведение undefined (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();
}

Ответ 3

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

num * num есть 56120*56120, который 3149454400, который переполняет a signed int, который вызывает поведение undefined.

Причина 40000 заключается в том, что 40000 * 40000 представляется в виде int.

Измените тип num на long long (или даже unsigned int)

Ответ 4

Это контрастирует с тем, что написали другие, но для меня изменение переменной num to size_t из int позволяет выделить. Может быть, num * num переполняет int для malloc. Выполнение malloc с 56120 * 56120 вместо num * num должно вызвать ошибку переполнения.

Ответ 5

float ls[3149454400]; - это массив с автоматическим типом хранения, который обычно выделяется в стеке процессов. Степень процесса по умолчанию ограничена значением, которое намного меньше 12 ГБ, которое вы пытаетесь нажать там. Таким образом, ошибка сегментации, которую вы наблюдаете, вызвана переполнением стека, а не malloc.