Могу ли я полагаться на malloc, возвращающий NULL?

17

Я прочитал, что в системах Unix malloc может возвращать указатель не-NULL, даже если память на самом деле недоступна, и попытка использовать память позже приведет к ошибке. Так как я не могу поймать такую ​​ошибку, проверив NULL, интересно, насколько полезно проверять NULL вообще?

В соответствующей заметке Herb Sutter говорит, что обработка ошибок памяти C ++ бесполезна, потому что система будет входить в спазмы подкачки задолго до того, как произойдет исключение. Это относится и к malloc ?

    
задан fredoverflow 30.10.2011 в 22:13
источник
  • Я думаю, что вы не должны использовать malloc в C ++: stackoverflow.com/questions/184537/... –  lc2817 30.10.2011 в 22:15
  • @ lc2817 вы должны использовать malloc только в том случае, если вы пишете код с C-интерфейсом (т. е. функции, которые должны использоваться с C, но написанные на C ++), и C-код отвечает за освобождение этой памяти. –  Oct 30 '11 at 21:17 30.10.2011 в 22:17
  • @WTP благодарит за эту точность. Хотя, я не знаю, так ли это здесь. –  lc2817 30.10.2011 в 22:21
  • @Dror K., я не понимаю цели щедрости, и быстрый поиск не помог. Вопрос уже имеет ответ, означает ли это, что вы ищете другой, улучшенный ответ? –  gsamaras 30.01.2016 в 22:21
  • @gsamaras Здравствуйте, я выбрал опцию, которая указывает, что существующий ответ достоин награды. Поэтому ответ на ваш вопрос заключается в том, что я хотел бы вознаградить существующий ответ, и я не ищу нового. –  Dror K. 30.01.2016 в 22:24

4 ответа

32

Цитирование руководств Linux :

  

По умолчанию Linux следует оптимистичной стратегии распределения памяти. Это означает, что когда malloc() возвращает non NULL , нет   гарантируем, что   память действительно доступна. Это очень плохая ошибка. В случае, если окажется, что в системе нет памяти, один или несколько   процессы будут   убитого печально известным убийцей OOM. В случае использования Linux в условиях, когда было бы менее желательно внезапно потерять   некоторые случайным образом   выбранных процессов, и, кроме того, версия ядра достаточно современна, можно отключить это чрезмерное поведение   используя команду:

# echo 2 > /proc/sys/vm/overcommit_memory

Вы должны проверить возврат NULL , особенно на 32-битных системах, поскольку адресное пространство процесса может быть исчерпано задолго до ОЗУ: например, на 32-битной Linux пользовательские процессы могут иметь полезное адресное пространство 2G - 3G, в отличие от более 4G общей памяти. В 64-битных системах может быть бесполезно проверять код возврата malloc , но в любом случае его можно считать хорошей практикой, и это делает вашу программу более переносимой. И, помните, разыменование нулевого указателя убивает ваш процесс, конечно; некоторые подкачки могут не сильно повредить по сравнению с этим.

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

Оператор C ++ по умолчанию, new , часто является оберткой по тем же механизмам распределения, что и malloc() .

    
ответ дан Antti Haapala 30.10.2011 в 22:17
  • +1 для цитирования хорошего разговора о том, как по умолчанию отключен Linux. Хорошая программа всегда должна проверять возвращаемое значение malloc. Если пользователь неправильно сконфигурировал свою систему (или оставил ее в разбитой конфигурации по умолчанию), то, конечно, это может не помочь, но вы ничего не можете сделать, и авария не отвечает вашей ответственности. Но если вы не можете проверить возвращаемое значение malloc, ваша программа будет ломаться даже при работе в системах, где пользователь / админ действительно заботится о правильности и отключил overcommit. Тогда пользователь, вероятно, рассмотрит вашу программу. :-) –  R.. 30.10.2011 в 22:54
  • Ну, правда немного сложнее, чем это. В адресном пространстве процесса есть дыры; например, программа может никогда не коснуться всех страниц в BSS или изменить страницу, отображаемую в сегменте данных. Недостаток, как правило, представляет собой большую проблему для настольной системы, чем overcommit. И раздел подкачки, если он включен, также предоставляет некоторую подушку, прежде чем все становится очень плохо. –  Antti Haapala 30.10.2011 в 23:02
  • Я не согласен. Недостаток - это не проблема, потому что вы всегда можете просто добавить в нее больше свопов. В любом случае, если у вас есть нетронутые страницы bss / data, это означает, что у вас есть глобальные переменные (а не только GOT / PLT там), что представляет большую проблему. :-) Возможно, некоторые из них необходимы, но более чем на одну страницу или две вещи почти наверняка свидетельствуют о проблемах дизайна ... –  R.. 31.10.2011 в 02:39
  • Система, дружественная к новичкам;) Единственный раз, когда мне приходилось иметь дело с убийцей OOM, был бы безудержный процесс, который в любом случае привел систему к остановке при замене. –  Antti Haapala 31.10.2011 в 11:18
5

В Linux вы действительно не можете полагаться на malloc , возвращающую NULL , если достаточная память недоступна из-за стратегии общего назначения ядра, но вы все равно должны ее проверить, потому что в некоторых случаях malloc будет return NULL , например когда вы запрашиваете больше памяти, чем доступно на машине в целом. Манифест Linux malloc(3) вызывает общую «очень плохую ошибку» и содержит советы о том, как отключить ее.

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

Что касается «спазмов пейджинга», это зависит от настройки машины. Например, я, как правило, не настраиваю раздел подкачки на портативных установках Linux, так как точное поведение, которое вы боитесь, может привести к удалению жесткого диска. Мне все равно понравятся программы C / C ++, которые я запускаю, чтобы проверить malloc возвращаемые значения, дать соответствующие сообщения об ошибках и, когда это возможно, очистить их после себя.

    
ответ дан Fred Foo 30.10.2011 в 22:18
  • Overcommit не является ни функцией, ни ошибкой, строго говоря. Это была просто историческая лень: overcommit намного проще реализовать, чем учет сборов за совершение. Предположительно, некоторые люди привыкли к этому, и ему понравилось (по каким-то порочным причинам), а некоторые даже начали писать программы, которые malloc 1gb были редкими массивами и еще более извращенными вещами, поэтому теперь мы застряли в том, что он включен по умолчанию. .. –  R.. 31.10.2011 в 02:41
2

Проверка возврата malloc сама по себе не поможет вам сделать ваши распределения более безопасными или менее подверженными ошибкам. Это может быть даже ловушка, если это единственный тест, который вы реализуете.

При вызове с аргументом 0 стандарт разрешает malloc возвращать уникальный адрес, который не является нулевым указателем и который у вас нет права доступа, тем не менее. Поэтому, если вы просто проверяете, является ли доход 0 , но не проверяет аргументы malloc , calloc или realloc , вы можете столкнуться с segfault намного позже.

Это условие ошибки (изнуренная память) встречается довольно редко в средах с размещением. Обычно у вас проблемы, задолго до того, как вы столкнулись с такой ошибкой. (Но если вы пишете библиотеки времени исполнения, это хакер ядра или создатель ракет, это совсем другое, и там тест имеет смысл.)

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

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

  • всегда инициализировать переменные всегда. для переменных указателя это имеет решающее значение, пусть программа краха приятно, прежде чем все станет слишком плохо. Неинициализированные члены указателя в struct s являются важной причиной ошибок, которые трудно найти.
  • всегда проверяйте аргумент malloc и Co., если это компиляция константа времени, например sizof toto , не может быть проблемой, но всегда убедитесь, что ваше векторное распределение правильно обрабатывает нулевой регистр.

Легкая проверка возврата malloc заключается в том, чтобы обернуть ее чем-то вроде memset(malloc(n), 0, 1) . Это просто записывает 0 в первом байте и успешно падает, если у malloc была ошибка, или n было 0 для начала.

    
ответ дан Jens Gustedt 30.10.2011 в 23:32
  • Позволяет просто сказать, что гораздо лучше сказать пользователю «Из кучи в строке foo», чем просто «исключение null-указателя в баре»; для него достаточно простой (макро?) обертки для malloc. Это в случае, если вы используете нелепые объемы памяти и можете ожидать использования более чем 2G на 32-битных системах. –  Antti Haapala 31.10.2011 в 09:24
1

Чтобы просмотреть это с альтернативной точки зрения:

" malloc может возвращать указатель не-NULL, даже если память фактически недоступна", не означает, что он всегда возвращает не-NULL. Там могут (и будут) случаи, когда NULL возвращается (как уже говорили другие), поэтому эта проверка все же необходима.

    
ответ дан glglgl 30.10.2011 в 22:27