Могу ли я полагаться на malloc, возвращающий NULL?
Я прочитал, что в Unix-системах malloc
может возвращать указатель не-NULL, даже если память фактически недоступна, и попытка использования памяти позже приведет к ошибке. Так как я не могу поймать такую ошибку, проверяя NULL, интересно, насколько полезно проверять NULL вообще?
В соответствующей заметке Херб Саттер говорит, что обработка ошибок памяти C ++ бесполезна, потому что система будет входить в спазмы пейджинга задолго до того, как на самом деле произойдет исключение. Это относится также к malloc
?
- Почему размер кеша L1 меньше, чем размер кэша L2 на большинстве процессоров?
- Как работают free и malloc в C?
- NodeJS: как отлаживать обнаруженную утечку памяти EventEmitter. 11 слушателей добавили "
- Выравнивание памяти в C-структурах
- Где в памяти мои переменные, хранящиеся в C?
- Получение максимальной пропускной способности на Haswell в кеше L1: получение только 62%
- Как узнать использование памяти моего приложения в Android?
- Звук перекрывается несколькими нажатиями кнопок
- Назначение выравнивания памяти
- Почему не указатели инициализируются с помощью NULL по умолчанию?
- MemoryCache Empty: возвращает null после установки
- Сколько памяти будет освобождено, если указатель изменится на C?
- Кто удаляет память, выделенную во время «новой» операции, которая имеет исключение в конструкторе?
Цитирование руководств Linux :
По умолчанию Linux следует оптимистичной страtagsи распределения памяти. Это означает, что когда
malloc()
возвращает неNULL
нет гарантии, что память действительно доступна. Это очень плохая ошибка. В случае, если окажется, что система не в памяти, один или несколько процессов будут убиты печально известным убийцей OOM. В случае использования Linux в ситуациях, когда было бы менее желательно внезапно потерять некоторые случайно выбранные процессы, и, кроме того, версия ядра достаточно современна, можно отключить это чрезмерное поведение, используя команду:# echo 2 > /proc/sys/vm/overcommit_memory
Вы должны проверить возврат NULL
, особенно на 32-битных системах, поскольку адресное пространство процесса может быть исчерпано задолго до ОЗУ: например, на 32-битной Linux пользовательские процессы могут иметь полезное адресное пространство 2G-3G, в отличие от более 4 ГБ общей оперативной памяти. В 64-битных системах было бы бесполезно проверять код возврата malloc
, но в любом случае можно считать хорошей практикой, и это сделает вашу программу более переносимой. И, помните, разыменование нулевого указателя убивает ваш процесс, конечно; некоторые подкачки могут не сильно повредить по сравнению с этим.
Если malloc
возвращает NULL
когда пытается выделить только небольшой объем памяти, тогда при попытке восстановить из условия ошибки нужно быть осторожным, так как любой последующий malloc
может также выйти из строя, пока не будет доступно достаточное количество памяти.
Оператор C ++ по умолчанию, используемый по умолчанию, часто является оберткой над теми же механизмами распределения, которые используются malloc()
.
В Linux вы действительно не можете полагаться на malloc
возвращающий NULL
если достаточная память недоступна из-за страtagsи общего назначения ядра, но вы все равно должны ее проверить, потому что в некоторых случаях malloc
вернет NULL
, например, когда вы запрашиваете больше памяти, чем доступных в машине в целом. Справочная система Linux malloc(3)
называет обход «действительно плохой ошибкой» и содержит рекомендации о том, как отключить ее.
Я никогда не слышал об этом поведении, которое также встречается в других вариантах Unix.
Что касается «спазмов пейджинга», это зависит от настройки машины. Например, я предпочитаю не устанавливать раздел подкачки на ноутбуках Linux, так как точное поведение, которое вы опасаетесь, может убить жесткий диск. Мне все равно понравятся программы C / C ++, которые я запускаю, чтобы проверить возвращаемые значения malloc
, дать соответствующие сообщения об ошибках и, по возможности, очистить их после себя.
Проверка возврата 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.
Чтобы просмотреть это с другой точки зрения:
malloc
” malloc
может возвращать указатель не-NULL, даже если память фактически недоступна”, не означает, что он всегда возвращает не-NULL. Там могут (и будут) случаи, когда NULL возвращается (как уже говорили другие), поэтому эта проверка необходима все же.