Как предотвратить сбой массивов переменной длины при нехватке памяти?

StackOverflow https://stackoverflow.com/questions/4151172

  •  08-10-2019
  •  | 
  •  

Вопрос

До того, как были поддержаны массивы переменной длины, я бы динамически распределял их следующим образом:

int foo(size_t n)
{
    int *arr = malloc(n * sizeof int);
    if (!arr) return ENOMEM; /* not enough memory */
    .
    . else do stuff with arr[]
    .
    free(arr);
    return 0;
}

С помощью массивов переменной длины теперь я могу сделать так, чтобы они выглядели чище:

int bar(size_t n)
{
    int arr[n];
    .
    . do stuff with arr[]
    .
    return 0;
}

Но теперь у меня нет проверки "не хватает памяти".На самом деле, программа завершает работу, если n слишком велико.

Как я могу изящно отказаться от bar (n), если n слишком велико?

Это было полезно?

Решение

Ситуация в точности не меняется по сравнению с любыми другими локальными переменными - объявление, подобное этому:

int baz(void)
{
    int arr[100000];
    .
    . do stuff with arr[]
    .
    return 0;
}

у него точно такая же проблема."Решение" такое же, каким оно было всегда - не повторяйте слишком глубоко и не выделяйте очень большие структуры данных с автоматическим сроком хранения (продолжайте использовать malloc() для этих случаев).Значение "очень большой" сильно зависит от вашего окружения.

Другими словами, не объявляйте int array[n]; если только ты не знаешь , что n ограничено разумным значением, таким, что вы были бы счастливы объявить массив такого максимального размера как обычный массив с неизменяемым типом.

(Да, это означает, что массивы с изменяемым типом не так полезны, как кажется на первый взгляд, поскольку вы очень мало выигрываете от простого объявления массива максимально необходимого размера).

Другие советы

Вы можете помешать им разбиться, не используя их. :)

Во всей серьезности почти нет безопасного способа использовать массивы переменной длины, чтобы упростить вашу жизнь, если у вас нет сильных границ на размере. С другой стороны, вы можете использовать их условно, так же, как это:

char vla_buf[n < 1000 ? n : 1];
char *buf = sizeof vla_buf < n ? malloc(n) : vla_buf;
if (!buf) goto error;
/* ... Do stuff with buf ... */
if (buf != vla_buf) free(buf);

Хотя это похоже на бесполезную боль, она может оказать огромную разницу производительности, особенно в резьбовых приложениях, где многие звонки malloc а также free может привести к конфликту блокировки. (Известная побочная выгода этого трюка заключается в том, что вы можете поддержать старые компиляторы без VLAS, просто заменяя [n < 1000 ? n : 1] с участием 1000, например, с макросом.)

Еще один непонятный случай, когда VLAS может быть полезен, находится в рекурсивных алгоритмах, где вы знаете, общее количество записей массивов, требуемых на всех уровнях рекурсии, ограничено n, куда n достаточно маленький, вы уверены, что не переполнено стек, но там, где может быть до n уровни рекурсии и отдельных уровней, которые используются до n элементы. До C99 единственный способ справиться с этим случаем без принятия n^2 пространство стека было использоваться malloc. Отказ С VLAS вы можете решить проблему целиком на стеке.

Имейте в виду, эти случаи, когда Влас действительно выгодны, довольно чертовски редко. Обычно VLA - это просто способ обмануть себя, что управление памятью легко, пока вы не получите бит по результирующимся (тривиально-эксплуатации) уязвимости, которые вы создали. :-)

Редактировать: Чтобы лучше удовлетворить оригинальный вопрос OP:

#define MAX_VLA 10000
int bar(size_t n)
{
    int arr[n <= MAX_VLA ? n : 1];
    if (sizeof arr/sizeof *arr < n) return ENOMEM;
    /* ... */
    return 0;
}

На самом деле это запрещено дорого проверить из условий памяти везде. Предпринимаемый способ борьбы с массивными данными состоит в том, чтобы ограничить размеры данных, определяя жесткую крышку по размеру на одном раннем контрольно-пропускном пункте и удалось быстро и изящно, когда крышка попала.

То, что я только что предложил, просто и глупый. Но то, что все обычные (не научные или специальные) продукты всегда делают. И его то, что обычно ожидается заказчиком.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top