Заключение вызовов в malloc () / realloc () ... это хорошая идея? - PullRequest
3 голосов
/ 29 ноября 2010

Для назначения мне нужно выделить динамический буфер, используя malloc() для исходного буфера и realloc(), чтобы расширить этот буфер, если это необходимо.Везде, где я использую (re | m) alloc (), код выглядит следующим образом:

char *buffer = malloc(size);

if (buffer == NULL) {
    perror();
    exit(EXIT_FAILURE);
}

Программа считывает только данные из файла и выводит их, поэтому я подумал, что нужно просто выйти из программы, когда| m) не удастся выделить, было бы хорошей идеей.Теперь реальный вопрос:

Было бы полезно обернуть вызовы, например, вот так?

void *Malloc(int size) {
    void *buffer = malloc(size);

    if (buffer == NULL) {
        perror();
        exit(EXIT_FAILURE);
    }

    return buffer;
}

Или это плохая идея?

Ответы [ 6 ]

4 голосов
/ 29 ноября 2010

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

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

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

Просто чтобы прояснить, однако, исчерпание памяти обычно происходит спустя долгое время после того, как ОС повреждена активностью перестановки страниц. Эта стратегия действительно полезна только для обнаружения нелепых распределений, например, для попытки malloc (a_small_negative_number) из-за ошибки.

4 голосов
/ 29 ноября 2010

Это плохая идея в представленной форме, потому что в любом другом , кроме простой программы, написанной для назначения, вы хотите сделать что-то более полезное / изящное, чем выручка. Так что лучше не заводить вредных привычек. Это не значит, что обертки вокруг выделения плохие как таковые (централизованная обработка ошибок может быть хорошей вещью), просто обертка, для которой вы не проверяете возвращаемое значение (например, это не вообще не возвращаться при неудаче) - плохая идея, если вы не предоставили какой-то механизм, позволяющий коду подключиться к логике спасения.

Если вы хотите сделать это в форме, которую вы представили, я настоятельно рекомендую использовать более явно другое имя, чем Malloc вместо malloc. Как malloc_or_die. : -)

3 голосов
/ 29 ноября 2010

Стоит ли мне обнаруживать ошибки OOM (нехватки памяти) в моем коде C?

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

3 голосов
/ 29 ноября 2010

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

void* malloc2(int size, int line_num){
    void *buffer = malloc(size);
    if (buffer == NULL) {
        printf("ERROR: cannot alloc for line %d\n", line_num);
        perror();
        exit(EXIT_FAILURE);
        }
    return buffer;
};

#define Malloc(n) malloc2((n), __LINE__)

РЕДАКТИРОВАТЬ: как уже упоминалось, это не хороший навык для опытного программиста, но для начинающего, который испытывает трудности даже с отслеживанием хода программы в «счастливом» случаеOK.

1 голос
/ 29 ноября 2010

Идеи о том, что «проверка на malloc на отказ бесполезна из-за чрезмерной загрузки» или что «ОС будет уже повреждена к моменту сбоя malloc», серьезно устарели. Надежные операционные системы никогда не перегружали память, а исторически не такие надежные (как Linux) в настоящее время имеют простые способы отключить чрезмерную загрузку и защитить систему от искажения из-за истощения памяти - , пока приложения выполняют свою роль не грохнуться и не сгореть при сбое malloc!

Существует множество причин, по которым malloc может дать сбой в современной системе:

  • Недостаточно физических ресурсов для создания экземпляра памяти.
  • Виртуальное адресное пространство исчерпано, даже когда имеется много свободной физической памяти. Это может легко произойти на 32-разрядной машине (или 32-разрядном пользовательском пространстве) с> 4 ГБ оперативной памяти + swap.
  • Фрагментация памяти. Если ваши схемы распределения очень плохие, вы можете получить 4 миллиона 16-байтовых блоков, разделенных равномерно на 1000 байтов, и неспособных удовлетворить вызов malloc(1024).

Как вы справляетесь с исчерпанием памяти, зависит от характера вашей программы.

Конечно, с точки зрения здоровья системы в целом, хорошо, что ваша программа умерла. Это уменьшает нехватку ресурсов и может позволить другим приложениям продолжать работу. С другой стороны, пользователь будет очень расстроен, если это будет означать потерю часов работы, редактирование видео, ввод бумаги, составление блога, кодирование и т. Д. Или они могут быть счастливы, если их mp3-плеер внезапно умирает из-за -память означает, что их диск перестает работать, и они могут вернуться к своему текстовому процессору и нажать «сохранить».

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

Гораздо лучшим решением будет просто возвращать ошибку вызывающей функции и позволять вызывающей функции возвращать ошибку своей вызывающей функции и т. Д., Пока вы не вернетесь обратно к main или подобному, где вы можете написать if (failure) exit(1);. Таким образом, код можно сразу же повторно использовать в других ситуациях, когда вам может понадобиться проверить ошибки и предпринять какие-то шаги по восстановлению, чтобы освободить память, сохранить / выгрузить ценные данные на диск и т. Д.

0 голосов
/ 29 ноября 2010

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

(я полагаю, вы пишете для размещенной среды, а не для встроенной, автономной.)

Современные системы с большим виртуальным адресным пространством будут просто никогда возвращать (void*)0 из malloc или realloc друг от друга, может быть, если аргументы были фиктивными. Вы столкнетесь с проблемами гораздо позже, когда ваша система начнет переключаться как сумасшедшая или даже закончится обменом.

Так что нет, не проверяйте возврат этих функций, это не имеет особого смысла. Вместо этого проверьте аргументы malloc против 0 (и для realloc, если оба 0 одновременно) с утверждением, поскольку тогда проблема не в malloc или realloc, а в том, как вы зову их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...