Безопасно ли удалять нулевой указатель?


299

Безопасно ли удалять нулевой указатель?

И это хороший стиль кодирования?


21
Хорошей практикой является написание программ на C ++ без единого обращения к delete. Вместо этого используйте RAII . То есть, используйте std::vector<T> v(100);вместо T* p = new T[100];, используйте умные указатели, такие как unique_ptr<T>и shared_ptr<T>которые заботятся об удалении вместо необработанных указателей и т. Д.
fredoverflow

8
благодаря make_shared(C ++ 11) и make_unique(C ++ 14) программа должна содержать нуль в newиdelete
sp2danny

2
Тем не менее, могут быть некоторые редкие случаи, которые требуют new / delete, например, atomic <T *>: atomic <unique_ptr <T >> не разрешен, а atomic <shared_ptr <T >> имеет издержки, которые в некоторых случаях могут быть неприемлемыми.
ATB

2
Чтобы объявить класс с управлением ресурсами с помощью RAII, вам нужно вызвать new и delete right?, Или вы говорите, что есть некоторый шаблонный класс, чтобы скрыть это даже это.
VinGarcia

2
@VinGarcia Дело в том, что большинству пользовательских / клиентских (то есть небиблиотечных ) кодов никогда не придется писать newили delete. Классы, разработанные для управления ресурсами, в которых стандартные компоненты не могут выполнять свою работу, могут, конечно, делать то, что им нужно, но дело в том, что они делают ужасные вещи с памятью, которой они управляют, а не с кодом конечного пользователя. Итак, создайте свой собственный класс библиотеки / помощника для do new/ deleteи используйте этот класс вместо них.
underscore_d

Ответы:


265

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

Я также хотел бы, чтобы deleteпо умолчанию для параметра было установлено значение NULL, как в

#define my_delete(x) {delete x; x = NULL;}

(Я знаю о значениях R и L, но было бы неплохо?)


72
Обратите внимание, что все еще могут быть несколько других указателей, указывающих на один и тот же объект, даже если вы устанавливаете один в NULL при удалении.
STH

13
В большинстве случаев в моем коде указатель выходит из области видимости после удаления. Намного безопаснее, чем просто установить его в NULL.
2010 года

142
Очень бог практика не устанавливая указатель на NULL после удаления. Установка указателя на NULL после удаления маскирует ошибки выделения памяти, что очень плохо. Программа, которая является правильной, не удаляет указатель дважды, и программа, которая действительно удаляет указатель дважды, должна аварийно завершить работу.
Деймон

15
@ Алиса: Неважно, что говорит стандарт в этом отношении. Стандарт определил, что удаление нулевого указателя допустимо по какой-то абсурдной причине 30 лет назад, поэтому это законно (скорее всего, наследие Си). Но удаление одного и того же указателя дважды (даже после изменения его битовой комбинации) все еще является серьезной программной ошибкой. Не по формулировке стандарта, а по логике программы и владению. Как и удаление нулевого указателя, поскольку нулевой указатель не соответствует ни одному объекту , поэтому ничего нельзя удалить. Программа должна точно знать, является ли объект действительным и кому он принадлежит, и когда он может быть удален.
Деймон

27
@Damon Тем не менее, несмотря на отмену ваших драконовских правил владения, структуры без блокировок доказуемо более надежны, чем структуры на основе блокировок. И да, мои коллеги действительно любят меня за расширенный профиль выполнения, который обеспечивают эти структуры, и за строгую безопасность потоков, которую они поддерживают, что позволяет легче рассуждать о коде (отлично подходит для обслуживания). Однако ни одно из этих действий, ни ваше подразумеваемое личное нападение не имеют отношения к определению правильности, обоснованности или права собственности. То, что вы предлагаете, - это хорошее эмпирическое правило, но оно не является универсальным законом и не закреплено в стандарте.
Алиса

77

Из проекта стандарта C ++ 0x.

$ 5.3.5 / 2 - "[...] В любой альтернативе значение операнда удаления может быть нулевым значением указателя. [... '"

Конечно, никто никогда не сможет «удалить» указатель со значением NULL, но это безопасно. В идеале не должно быть кода, который удаляет нулевой указатель. Но иногда это полезно, когда удаление указателей (например, в контейнере) происходит в цикле. Поскольку удаление значения указателя NULL безопасно, можно действительно написать логику удаления без явных проверок на удаление операнда NULL.

Кроме того, C Standard $ 7.20.3.2 также говорит, что «free» для указателя NULL не выполняет никаких действий.

Свободная функция вызывает освобождение пространства, на которое указывает ptr, то есть делает его доступным для дальнейшего выделения. Если ptr является нулевым указателем, никаких действий не происходит.


2
Мне бы очень хотелось, чтобы этот ответ содержался в цитатах, если он намеренно не привел к неэффективности неоптимизированного кода. Как говорится в принятом ответе, удаление нулевого указателя не допускается. Поэтому проверка, является ли указатель нулевым, перед его удалением является абсолютно посторонним.
Codetaku

47

Да, это безопасно.

Нет ничего плохого в удалении нулевого указателя; это часто уменьшает количество тестов в хвосте функции, если нераспределенные указатели инициализируются нулями, а затем просто удаляются.


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

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Существуют всевозможные гниды, которые можно выбрать с помощью примера кода, но концепция (я надеюсь) ясна. Переменные-указатели инициализируются нулями, поэтому deleteоперациям в конце функции не нужно проверять, не являются ли они ненулевыми в исходном коде; код библиотеки выполняет эту проверку в любом случае.


Я должен был прочитать это несколько раз, чтобы понять это. Вы должны иметь ввиду инициализацию их нулями в начале метода или во время, а не в хвосте, конечно? В противном случае вы просто удалите как обнуление, так и удаление.
Маркиз Лорн

1
@EJP: А , не полностью неправдоподобна контур функции может быть: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Я не показал никакой блочной структуры или переходов, но delete[]операции в конце безопасны из-за инициализации в начале. Если что-то подпрыгнуло до конца после того, как x1было выделено, и до того, как y1было выделено, и не было инициализации y1, тогда было бы неопределенное поведение - и хотя код мог проверять на нулевое значение (of x1и y1) перед удалением, нет необходимости делать так.
Джонатан Леффлер

22

Удаление нулевого указателя не имеет никакого эффекта. Это не очень хороший стиль кодирования, потому что он не нужен, но и неплох.

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


20
время, когда люди хотят удалить указатель NULL, это когда они не уверены, содержит ли он NULL ... если бы они знали, что это NULL, они бы не рассматривали удаление и, следовательно, спрашивали ;-).
Тони Делрой

@ Тони: Моя точка зрения состояла только в том, что это не будет иметь никакого эффекта, и наличие такого кода, который удаляет указатель, который иногда содержит NULL, не обязательно плох.
Брайан Р. Бонди

3
IMO избыточные проверки, безусловно, плохи для производительности, удобочитаемости и ремонтопригодности.
Пол

@paulm OP, конечно, не говорит о таких плохих вещах, скорее о Seg Fault / UB.
Зима


3

Это безопасно, если вы не перегружены оператором удаления. если вы перегружаете оператор удаления и не обрабатывает нулевое условие, то это вовсе не безопасно.


Не могли бы вы добавить какое-либо объяснение вашего ответа?
Марцин Набиалек,

-2

Я испытал , что это не безопасно (VS2010) , чтобы удалить [] NULL (т.е. синтаксис массива). Я не уверен, соответствует ли это стандарту C ++.

Он является безопасным для удаления NULL (скалярная синтаксис).


6
Это незаконно, и я не верю в это.
Конрад Рудольф

5
Вы должны быть в состоянии удалить любой нулевой указатель. Так что, если он ломается для вас, то, вероятно, в коде есть ошибка.
Мистик

3
§5.3.2 Во втором альтернативном варианте (массив удаления) значением операнда удаления может быть значение нулевого указателя или значение указателя, полученное из предыдущего выражения нового массива.
sp2danny

1
@Opux Поведение VS2010 заявлено в ответе. Как объяснено в других комментариях, это безопасно delete[] NULL.
Конрад Рудольф,

1
@Opux Вот почему я написал «я не верю в это», а не «это неправильно». Но я все еще не знаю, и это было бы довольно вопиющим, глупым нарушением стандарта. На самом деле VC ++ довольно хорошо соблюдает ограничения стандарта, и места, где он их нарушает, имеют исторический смысл.
Конрад Рудольф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.