В моей компании есть правило кодирования, которое гласит, что после освобождения памяти сбросьте переменную в NULL
. Например ...
void some_func ()
{
int *nPtr;
nPtr = malloc (100);
free (nPtr);
nPtr = NULL;
return;
}
Я чувствую, что в случаях, подобных приведенному выше коду, установка значения NULL
не имеет никакого значения. Или я что-то упустил?
Если в таких случаях нет смысла, я собираюсь обсудить это с «командой качества», чтобы удалить это правило кодирования. Пожалуйста посоветуй.
c
coding-style
malloc
free
heap-memory
Alphaneo
источник
источник
ptr == NULL
прежде чем что-либо делать с ним. Если вы не аннулируете свои свободные указатели, вы получите,ptr != NULL
но все еще неиспользуемый указатель.Ответы:
Установка неиспользуемых указателей в NULL - это защитный стиль, защищающий от ошибок висячих указателей. Если после освобождения доступен свисающий указатель, вы можете прочитать или перезаписать случайную память. При обращении к нулевому указателю на большинстве систем происходит немедленный сбой, сразу же сообщая, в чем заключается ошибка.
Для локальных переменных может быть немного бессмысленно, если «очевидно», что указатель больше не доступен после освобождения, поэтому этот стиль больше подходит для данных-членов и глобальных переменных. Даже для локальных переменных это может быть хорошим подходом, если функция продолжается после освобождения памяти.
Чтобы завершить стиль, вы должны также инициализировать указатели в NULL, прежде чем им будет присвоено истинное значение указателя.
источник
int *nPtr=NULL;
. Теперь, я бы согласился, что это было бы излишним, с malloc, следующим прямо в следующей строке. Однако, если есть код между объявлением и первой инициализацией, кто-то может начать использовать переменную, даже если она еще не имеет значения. Если вы инициализируете null, вы получите segfault; без, вы могли бы снова читать или писать случайную память. Аналогично, если переменная впоследствии инициализируется только условно, последующие ошибочные обращения должны дать вам мгновенные сбои, если вы вспомнили об инициализации null.Установка указателя на
NULL
afterfree
является сомнительной практикой, которая часто популяризируется как правило «хорошего программирования» на явно ложной предпосылке. Это одна из тех фальшивых истин, которые относятся к категории «звучит правильно», но в действительности не достигают абсолютно ничего полезного (и иногда приводят к негативным последствиям).Предположительно, установка указателя на
NULL
afterfree
должна предотвращать страшную проблему «двойного освобождения», когда одно и то же значение указателя передаетсяfree
более одного раза. В действительности, однако, в 9 случаях из 10 реальная проблема «двойного освобождения» возникает, когда в качестве аргументов используются разные объекты указателя, содержащие одно и то же значение указателяfree
. Нет необходимости говорить, что установка указателя наNULL
after неfree
дает абсолютно ничего, чтобы предотвратить проблему в таких случаях.Конечно, при использовании одного и того же объекта-указателя в качестве аргумента можно столкнуться с проблемой «двойного освобождения»
free
. Однако в реальности подобные ситуации обычно указывают на проблему с общей логической структурой кода, а не на случайное «двойное освобождение». Надлежащим способом решения проблемы в таких случаях является пересмотр и переосмысление структуры кода, чтобы избежать ситуации, когда один и тот же указатель передаетсяfree
более одного раза. В таких случаях установка указателяNULL
на проблему и решение проблемы «исправлено» - не более чем попытка скрыть проблему. Это просто не будет работать в общем случае, потому что проблема со структурой кода всегда найдет другой способ проявить себя.Наконец, если ваш код специально разработан для того, чтобы полагаться на значение указателя
NULL
или нетNULL
, то прекрасно установить значение указателя наNULL
afterfree
. Но в качестве общего правила «хорошей практики» (например, «всегда устанавливайте указатель наNULL
послеfree
»), это, опять же, хорошо известная и довольно бесполезная подделка, за которой часто следуют некоторые по чисто религиозным, вуду-подобным причинам.источник
foo* bar=getFoo(); /*more_code*/ free(bar); /*more_code*/ return bar != NULL;
. Здесь, установивbar
дляNULL
после вызоваfree
будет вызывать функцию думать , что никогда не было бара и вернуть неправильное значение!Большинство ответов были направлены на предотвращение двойного освобождения, но установка указателя на NULL имеет еще одно преимущество. После освобождения указателя эта память становится доступной для перераспределения другим вызовом malloc. Если у вас все еще есть оригинальный указатель, вы можете получить ошибку, при которой вы попытаетесь использовать указатель после освобождения и испортить какую-то другую переменную, а затем ваша программа войдет в неизвестное состояние, и могут произойти все виды плохих вещей (сбой, если вы повезло, повреждение данных, если вам не повезло). Если вы установили указатель на NULL после освобождения, любая попытка чтения / записи через этот указатель позже приведет к segfault, что обычно предпочтительнее случайного повреждения памяти.
По обеим причинам может быть хорошей идеей установить указатель на NULL после free (). Это не всегда необходимо, хотя. Например, если переменная-указатель выходит из области видимости сразу после free (), нет особой причины устанавливать ее в NULL.
источник
free
, но это действительно имеет смысл.Это считается хорошей практикой, чтобы избежать перезаписи памяти. В приведенной выше функции это не нужно, но часто, когда это делается, он может обнаружить ошибки приложения.
Попробуйте что-то вроде этого:
DEBUG_VERSION позволяет вам профилировать освобождения в коде отладки, но оба они функционально одинаковы.
Редактировать : Добавлено do ... в то время как предложено ниже, спасибо.
источник
do { } while(0)
блоке, чтобыif(x) myfree(x); else dostuff();
не разрываться.do {X} while (0)
- это IMO - лучший способ создать макро-тело, которое «чувствует и работает как» функция. В любом случае, большинство компиляторов оптимизируют цикл.Если вы достигнете указателя, который был свободен () d, он может сломаться или нет. Эта память может быть перераспределена в другую часть вашей программы, и тогда вы получите повреждение памяти,
Если вы установите указатель на NULL, то при обращении к нему программа всегда завершается сбоем с ошибкой. Не более ,, иногда это работает '', не более ,, сбой непредсказуемым образом ''. Это намного проще для отладки.
источник
Установка указателя на
free
память «d» означает, что любая попытка доступа к этой памяти через указатель немедленно приведет к сбою, а не к неопределенному поведению. Намного легче определить, где что-то пошло не так.Я вижу ваш аргумент: так
nPtr
как сразу выходит из области видимостиnPtr = NULL
, кажется, нет причины для его установкиNULL
. Однако, в случаеstruct
члена или где-то еще, где указатель не сразу выходит из области видимости, это имеет больше смысла. Не сразу очевидно, будет ли этот указатель снова использоваться кодом, который не должен его использовать.Вероятно, правило сформулировано без проведения различий между этими двумя случаями, поскольку гораздо сложнее автоматически применять правило, не говоря уже о том, чтобы разработчики следовали ему. Это не повредит устанавливать указатели
NULL
после каждой бесплатной, но он может указывать на большие проблемы.источник
самая распространенная ошибка в c - это двойное освобождение. В основном вы делаете что-то подобное
и это в конечном итоге довольно плохо, ОС пытается освободить часть уже освобожденной памяти и, как правило, это segfault. Поэтому рекомендуется установить значение
NULL
, чтобы вы могли выполнить тестирование и проверить, действительно ли вам нужно освободить эту память.Также следует отметить, что
free(NULL)
ничего не будет делать, поэтому вам не нужно писать оператор if. Я на самом деле не гуру ОС, но я симпатичен даже сейчас, когда большинство ОС вылетает при двойном освобождении.Это также главная причина того, что все языки со сборкой мусора (Java, dotnet) так гордились отсутствием этой проблемы, а также тем, что разработчикам не нужно оставлять управление памятью в целом.
источник
p = (char *)malloc(.....); free(p); if(p!=null) //p!=null is true, p is not null although freed { free(p); //Note: checking doesnot prevent error here }
free(void *ptr)
не может изменить значение указателя это передается. Он может изменить содержимое указателя, данные, хранящиеся по этому адресу , но не сам адрес или значение указателя . Для этого потребуетсяfree(void **ptr)
(что, очевидно, не разрешено стандартом) или макрос (который разрешен и идеально переносим, но людям не нравятся макросы). Кроме того, C не об удобстве, а о предоставлении программистам столько контроля, сколько они хотят. Если они не хотят дополнительных накладных расходов на установку указателейNULL
, их не следует навязывать им.free
» (наряду с такими вещами, как «приведение результата функций выделения памяти» или «бездумное использование имен типов сsizeof
»).Идея, стоящая за этим, заключается в том, чтобы остановить случайное повторное использование освобожденного указателя.
источник
Это (может) быть действительно важным. Хотя вы освобождаете память, более поздняя часть программы может выделить что-то новое, что случится в космосе. Ваш старый указатель теперь будет указывать на допустимый кусок памяти. Тогда возможно, что кто-то будет использовать указатель, что приведет к неверному состоянию программы.
Если вы НЕ УСТАНАВЛИВАЕТЕ указатель, то любая попытка его использования будет разыменовываться 0x0 и сразу же завершаться сбоем, что легко отладить. Случайные указатели, указывающие на случайную память, трудно отлаживать. Очевидно, что в этом нет необходимости, но именно поэтому он содержится в документе с рекомендациями.
источник
Из стандарта ANSI C:
«неопределенное поведение» - это почти всегда сбой программы. Чтобы избежать этого, можно безопасно сбросить указатель на NULL. Сам по себе free () не может этого сделать, поскольку ему передается только указатель, а не указатель на указатель. Вы также можете написать более безопасную версию free () с нулевым указателем:
источник
NULL
чтобы избежать ошибок маскировки. stackoverflow.com/questions/1025589/… Кажется, что в любом случае некоторые ошибки будут скрыты.Я считаю, что это мало помогает, так как по моему опыту, когда люди получают доступ к свободному распределению памяти, это почти всегда, потому что у них где-то есть другой указатель. И тогда это вступает в противоречие с другим стандартом персонального кодирования, который называется «Избегайте бесполезных помех», поэтому я этого не делаю, так как считаю, что это редко помогает и делает код немного менее читабельным.
Тем не менее - я не буду устанавливать переменную в null, если указатель не предполагается использовать снова, но часто дизайн более высокого уровня дает мне причину установить его в null в любом случае. Например, если указатель является членом класса, и я удалил то, на что он указывает, тогда «контракт», если вам нравится класс, заключается в том, что этот член будет указывать на что-то допустимое в любое время, поэтому для него должно быть установлено значение null. по этой причине. Небольшое различие, но я считаю важным.
В С ++ важно всегда думать о том, кому принадлежат эти данные, когда вы выделяете некоторую память (если вы не используете умные указатели, но даже тогда требуется некоторая мысль). И этот процесс приводит к тому, что указатели, как правило, являются членами некоторого класса, и, как правило, вы хотите, чтобы класс всегда находился в допустимом состоянии, и самый простой способ сделать это - установить переменную-член в значение NULL, чтобы указать, что он указывает сейчас ничего.
Распространенным шаблоном является установка для всех указателей-членов значения NULL в конструкторе и удаление вызова деструктора для любых указателей на данные, которые, согласно вашему проекту, принадлежат этому классу . Очевидно, что в этом случае вы должны установить указатель на NULL, когда вы удаляете что-то, чтобы указать, что вы не владеете никакими данными раньше.
Подводя итог, да, я часто устанавливаю указатель на NULL после удаления чего-либо, но это как часть более крупного проекта и размышлений о том, кто владеет данными, а не из-за слепого следования стандартному правилу кодирования. Я бы не стал этого делать в вашем примере, так как считаю, что делать это бесполезно, и это добавляет «беспорядок», который, по моему опыту, так же ответственен за ошибки и плохой код, как и подобные вещи.
источник
Недавно я столкнулся с тем же вопросом после того, как искал ответ. Я пришел к такому выводу:
Это лучшая практика, и необходимо следовать ей, чтобы сделать ее переносимой на все (встроенные) системы.
free()
является библиотечной функцией, которая изменяется при изменении платформы, поэтому не следует ожидать, что после передачи указателя на эту функцию и после освобождения памяти этот указатель будет установлен в NULL. Это может не относиться к некоторым библиотекам, реализованным для платформы.так что всегда идти на
источник
Это правило полезно, когда вы пытаетесь избежать следующих сценариев:
1) У вас очень длинная функция со сложной логикой и управлением памятью, и вы не хотите случайно повторно использовать указатель на удаленную память позже в этой функции.
2) Указатель является переменной-членом класса, которая имеет довольно сложное поведение, и вы не хотите случайно повторно использовать указатель на удаленную память в других функциях.
В вашем сценарии это не имеет особого смысла, но если бы функция стала длиннее, это могло бы иметь значение.
Вы можете утверждать, что установка его в NULL может на самом деле замаскировать логические ошибки позже, или в случае, если вы предполагаете, что он действителен, вы все равно аварийно завершите работу на NULL, так что это не имеет значения.
В общем, я бы посоветовал вам установить его в NULL, если вы считаете, что это хорошая идея, и не беспокоиться, если вы считаете, что оно того не стоит. Вместо этого сосредоточьтесь на написании коротких функций и хорошо разработанных классов.
источник
Чтобы добавить к тому, что сказали другие, один хороший метод использования указателя - всегда проверять, является ли он действительным указателем или нет. Что-то вроде:
Явная маркировка указателя как NULL после освобождения позволяет использовать этот тип в C / C ++.
источник
Это может быть больше аргумент для инициализации всех указателей в NULL, но что-то вроде этого может быть очень хитрой ошибкой:
p
заканчивается в том же месте в стеке, что и предыдущийnPtr
, поэтому он все еще может содержать, казалось бы, действительный указатель. Назначение*p
может перезаписать все виды несвязанных вещей и привести к ужасным ошибкам. Особенно, если компилятор инициализирует локальные переменные с нулем в режиме отладки, но не включает оптимизацию. Таким образом, отладочные сборки не показывают никаких признаков ошибки, в то время как сборки выпуска взрываются случайным образом ...источник
Установка только что освобожденного указателя в NULL не обязательна, но это хорошая практика. Таким образом, вы можете избежать 1) использовать освобожденный остроконечный 2) освободить его
источник
Установка указателя на NULL для защиты от так называемого двойного освобождения - ситуация, когда free () вызывается более одного раза для одного и того же адреса без перераспределения блока по этому адресу.
Двойное освобождение приводит к неопределенному поведению - обычно это приводит к повреждению кучи или немедленному падению программы. Вызов free () для указателя NULL ничего не делает и поэтому гарантированно безопасен.
Поэтому, если вы сейчас не уверены, что указатель покидает область действия сразу или очень скоро после free (), рекомендуется установить для этого указателя значение NULL, чтобы, даже если free () вызывался снова, теперь он вызывался для указателя NULL и неопределенного поведения. уклоняется
источник
Идея состоит в том, что, если вы попытаетесь разыменовать недействительный указатель после его освобождения, вы захотите потерпеть неудачу (segfault), а не молча и загадочно.
Но будь осторожен. Не все системы вызывают segfault, если вы разыменовываете NULL. В (по крайней мере, в некоторых версиях) AIX, * (int *) 0 == 0, а Solaris имеет дополнительную совместимость с этой "функцией" AIX.
источник
На первоначальный вопрос: установка указателя на NULL непосредственно после освобождения содержимого - это пустая трата времени при условии, что код соответствует всем требованиям, полностью отлажен и никогда не будет изменен. С другой стороны, защита NULL от освобожденного указателя может быть весьма полезна, когда кто-то бездумно добавляет новый блок кода под free (), когда дизайн исходного модуля неверен, и в случае этого -компилирует, но не делает то, что я хочу ошибки.
В любой системе есть недостижимая цель - сделать ее проще и правильнее, а также снизить стоимость неточных измерений. В Си мы предлагаем набор очень острых, очень сильных инструментов, которые могут создать много вещей в руках квалифицированного рабочего и нанести всевозможные метафорические травмы при неправильном обращении. Некоторые из них трудно понять или использовать правильно. И люди, естественно, склонные к риску, делают иррациональные вещи, такие как проверка указателя на значение NULL перед тем, как вызвать его бесплатно…
Проблема измерения состоит в том, что всякий раз, когда вы пытаетесь отделить хорошее от менее хорошего, чем сложнее случай, тем выше вероятность получения неоднозначных измерений. Если цель состоит в том, чтобы сохранить только хорошие практики, то некоторые неоднозначные из них выбрасываются с действительно плохими. Если ваша цель - устранить плохое, тогда двусмысленность может остаться с хорошим. Эти две цели: сохранить только хорошее или устранить явно плохое - кажутся диаметрально противоположными, но обычно существует третья группа, которая не является ни той, ни другой, некоторые из обеих.
Прежде чем обращаться в отдел качества, попробуйте просмотреть базу данных ошибок, чтобы увидеть, как часто, если вообще когда-либо, неверные значения указателя вызывали проблемы, которые нужно было записать. Если вы хотите изменить ситуацию к лучшему, определите наиболее распространенную проблему в рабочем коде и предложите три способа ее предотвращения.
источник
Есть две причины:
Избегайте сбоев при двойном освобождении
Написано RageZ в двойном вопросе .
Избегайте использования уже освобожденных указателей
Написано Мартином против Лёвиса в другом ответе .
источник
Поскольку у вас есть команда по обеспечению качества, позвольте мне добавить небольшое замечание о QA. Некоторые автоматизированные инструменты QA для C помечают назначения для освобожденных указателей как «бесполезное назначение
ptr
». Например, PC-lint / FlexeLint от Gimpel Software сообщаетtst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used
Существуют способы выборочного подавления сообщений, поэтому вы все равно можете удовлетворить оба требования по обеспечению качества, если ваша команда примет такое решение.
источник
Всегда желательно объявить переменную-указатель с NULL, например,
Скажем, ptr указывает на адрес памяти 0x1000 . После использования
free(ptr)
всегда желательно обнулить переменную указателя, объявив снова NULL . например:Если не объявлено повторно в NULL , переменная-указатель продолжает указывать на тот же адрес ( 0x1000 ), эта переменная-указатель называется висячим указателем . Если вы определяете другую переменную-указатель (скажем, q ) и динамически выделяете адрес для нового указателя, существует вероятность получения того же адреса ( 0x1000 ) новой переменной-указателем. Если в этом случае вы используете один и тот же указатель ( ptr ) и обновляете значение по адресу, указанному тем же указателем ( ptr ), то программа в конечном итоге записывает значение в то место, куда указывает q (поскольку p и 0x1000 ) ). q является указывая на тот же адрес (
например
источник
Короче говоря: вы не хотите случайно (по ошибке) получить доступ к адресу, который вы освободили. Потому что, когда вы освобождаете адрес, вы разрешаете ему выделять этот адрес в куче другому приложению.
Однако, если вы не установите указатель на NULL и по ошибке попытаетесь отменить ссылку на указатель или изменить значение этого адреса; ВЫ МОЖЕТЕ ЕЩЕ ДЕЛАТЬ. НО НЕ ЧТО-ТО, ЧТО ВЫ ЛОГИЧЕСКИ ХОТИТЕ СДЕЛАТЬ.
Почему я все еще могу получить доступ к памяти, которую я освободил? Потому что: у вас может быть свободная память, но переменная-указатель все еще содержала информацию об адресе кучи памяти. Итак, в качестве защитной стратегии, пожалуйста, установите ее в NULL.
источник