Установка переменной в NULL после free

в моей компании есть правило кодирования, которое говорит, что после освобождения любой памяти сбросьте переменную в NULL. Например...

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

Я чувствую, что в случаях, подобных показанному выше коду, значение NULL не имеет никакого значения. Или я что-то упускаю?

Если в таких случаях нет смысла, я собираюсь обсудить это с "командой качества", чтобы удалить это правило кодирования. Пожалуйста, совет.

23 ответов


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

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

чтобы завершить стиль, вы также должны инициализировать указатели на NULL, прежде чем им будет присвоено значение true pointer.


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

предположительно, установка указателя на NULL после free должен предотвратить страшную" двойную свободную " проблему, когда одно и то же значение указателя перешел в free более одного раза. На самом деле, однако, в 9 случаях из 10 Реальная "двойная свободная" проблема возникает, когда разные объекты указателя, имеющие одинаковое значение указателя, используются в качестве аргументов для free. Излишне говорить, что установка указателя на NULL после free не достигает абсолютно ничего, чтобы предотвратить проблему в таких случаях.

конечно, можно столкнуться с проблемой "double free" при использовании того же объекта указателя в качестве аргумента для free. Однако на самом деле подобные ситуации обычно указывают на проблему с общей логической структурой кода, а не просто на случайную "двойную свободу". Правильный способ решения проблемы в таких случаях-пересмотреть и переосмыслить структуру кода, чтобы избежать ситуации, когда один и тот же указатель передается free более одного раза. В таких случаях установка указателя на NULL и рассмотрение проблемы "исправлено" - это не что иное, как попытка подмести проблему под ковер. В общем случае это просто не сработает, потому что проблема со структурой кода всегда найдет другой способ проявить себя.

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


большинство ответов были сосредоточены на предотвращении двойного свободного, но установка указателя на NULL имеет еще одно преимущество. Как только вы освободите указатель, эта память будет доступна для перераспределения другим вызовом malloc. Если у вас все еще есть исходный указатель, вы можете получить ошибку, в которой вы пытаетесь использовать указатель после освобождения и повреждения какой-либо другой переменной, а затем ваша программа переходит в неизвестное состояние, и все виды плохих вещей могут произойти (сбой, Если Вам ПОВЕЗЕТ, данные коррупция, если вам не повезет). Если вы установили указатель на NULL после free, любая попытка чтения / записи через этот указатель позже приведет к segfault, что обычно предпочтительнее случайного повреждения памяти.

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


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

попробуйте что-то вроде этого:

#if DEBUG_VERSION
void myfree(void **ptr)
{
    free(*ptr);
    *ptr = null;
}
#else
#define myfree(p) do { void ** __p = (p); free(*(__p)); *(__p) = null; } while (0)
#endif

DEBUG_VERSION позволяет профилировать свободные в отладочном коде, но оба функционально одинаковы.

редактировать: добавлено do ... хотя как предлагается ниже, Спасибо.


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

Если вы установите указатель на NULL, то при доступе к нему программа всегда аварийно завершает работу с segfault. Не больше, иногда это работает", не больше,, падает непредсказуемым образом". Это проще отлаживать.


установка указателя на free'D память означает, что любая попытка доступа к этой памяти через указатель немедленно завершится сбоем, а не вызовет неопределенное поведение. Это значительно облегчает определение того, где все пошло не так.

Я вижу ваш аргумент: с nPtr выходит из области видимости сразу после nPtr = NULL, похоже, нет причин устанавливать его в NULL. Однако в случае a struct член или где-то еще, где указатель не сразу выходя за рамки, это имеет больше смысла. Не сразу видно, будет ли этот указатель использоваться снова кодом, который не должен его использовать.

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


наиболее распространенной ошибкой в c является double free. В основном вы делаете что-то подобное

free(foobar);
/* lot of code */
free(foobar);

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

if(foobar != null){
  free(foobar);
}

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

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


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


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

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


из стандарта ANSI C:

void free(void *ptr);

функция вызывает пространство указывает ptr, который будет освобожден, то есть, доступны для дальнейшего распределение. Если PTR является указателем null, никаких действий не происходит. В противном случае, если аргумент не соответствует указателю ранее возвращенной памятью , malloc, или функция realloc, или если пространство было освобождено вызов free или realloc, поведение есть не определено.

"неопределенное поведение" почти всегда является сбоем программы. Чтобы избежать этого, можно сбросить указатель на NULL. free () сам по себе не может этого сделать, поскольку он передается только указателем, а не указателем на указатель. Вы также можете написать более безопасную версию free (), которая обнуляет указатель:

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}

Я считаю, что это мало помогает, как в моем опыте, когда люди получают доступ к освобожденному распределению памяти, это почти всегда потому, что у них есть другой указатель на него где-то. И тогда он конфликтует с другим личным стандартом кодирования, который "избегает бесполезного беспорядка", поэтому я не делаю этого, поскольку я думаю, что это редко помогает и делает код немного менее читаемым.

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

В c++ важно всегда думать, кто принадлежит эти данные, когда вы выделяете память (если вы не используете смарт-указатели, но даже тогда требуется некоторая мысль). И этот процесс, как правило, приводит к указателям, как правило, являющимся членом некоторого класса, и обычно вы хотите, чтобы класс был в допустимом состоянии во все времена, и самый простой способ сделать это-установить переменную-член в NULL, чтобы указать, что она указывает ни на что сейчас.

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

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


недавно я столкнулся с тем же вопросом после того, как я искал ответ. Я пришел к такому выводу:--3-->

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

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

так всегда

free(ptr);
ptr = NULL;

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

1) у вас есть очень длинная функция со сложной логикой и управлением памятью, и вы не хотите случайно повторно использовать указатель на удаленную память позже в функции.

2) указатель является переменной-членом класса, который имеет довольно сложное поведение, и вы не хотите случайно повторно использовать указатель на удаленную память в других функциях.

в вашем случае, это не имеет большого смысла, но если бы функция стала длиннее, это могло бы иметь значение.

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

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


чтобы добавить к тому, что сказали другие, один хороший метод использования указателя-всегда проверять, является ли он допустимым указателем или нет. Что-то вроде:


if(ptr)
   ptr->CallSomeMethod();

явная маркировка указателя как NULL после освобождения позволяет использовать этот тип в C / C++.


это может быть больше аргументом для инициализации всех указателей на NULL, но что-то вроде этого может быть очень скрытой ошибкой:

void other_func() {
  int *p; // forgot to initialize
  // some unrelated mallocs and stuff
  // ...
  if (p) {
    *p = 1; // hm...
  }
}

void caller() {
  some_func();
  other_func();
}

p заканчивается в том же месте в стеке, что и прежний nPtr, Так что все еще может содержать, казалось бы, действительный указатель. Назначение *p может перезаписать все виды несвязанных вещей и привести к уродливым ошибкам. Особенно, если компилятор инициализирует локальные переменные с нулем в режиме отладки, но не включает оптимизацию. Так сборки отладки не показывают никаких признаков ошибки, в то время как сборки выпуска взрываются случайным образом...


установка указателя, который только что был освобожден до NULL, не является обязательной, но хорошей практикой. Таким образом, вы можете избежать 1)используя свободную точку 2) освободите ее towice


Settings указатель на NULL предназначен для защиты agains, так называемых double-free - ситуация, когда free() вызывается более одного раза для одного и того же адреса без перераспределения блока по этому адресу.

Double-free приводит к неопределенному поведению-обычно повреждение кучи или немедленно сбой программы. Вызов free () для нулевого указателя ничего не делает и поэтому гарантированно безопасен.

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


есть две причины:

избегайте сбоев при двойном освобождении

написано Рагез на дублировать вопрос.

наиболее распространенной ошибкой в c является двойная бесплатный. В основном вы делаете что-то вроде это

free(foobar);
/* lot of code */
free(foobar);

и это в конечном итоге довольно плохо, ОС попробовать освободить уже освобожденную память и как правило, это обработка выхода онлайн / оффлайн. Так хорошо практика является для NULL, Так что вы можете сделать тест и проверить, если вы действительно нужно освободить эту память

if(foobar != null){
  free(foobar);
}

также следует отметить, что free(NULL) я ничего не буду делать, так что тебе не придется. напишите оператор if. Я не действительно гуру OS, но я довольно даже теперь большинство ОС рухнет на двойном бесплатный.

Это также основная причина, почему все языки со сборкой мусора (Java, dotnet) так гордился тем, что не имея эту проблему, а также не необходимость оставить разработчику память управление в целом.

избегайте использования уже освобожденных указателей

написано Мартин В. Löwis на еще один ответ.

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

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

чтобы завершить стиль, вы также должны инициализировать указатели на NULL до им присваивается истинный указатель значение.


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

но... быть осторожным. Не все системы вызывают segfault, если вы разыменовали NULL. В(по крайней мере, некоторых версиях) AIX, *(int *)0 == 0, а Solaris имеет необязательную совместимость с этой функцией AIX."


к исходному вопросу: Установка указателя на NULL непосредственно после освобождения содержимого является пустой тратой времени, при условии, что код соответствует всем требованиям, полностью отлажен и никогда не будет изменен снова. С другой стороны, защитное обнуление освобожденного указателя может быть очень полезно, когда кто-то бездумно добавляет новый блок кода под free (), когда дизайн исходного модуля неправильный, и в случае it-compiles-but-doesn't-do-what-I-want жуки.

в любой системе есть недостижимая цель сделать ее проще к правильной вещи и неприводимой стоимости неточных измерений. В " С " нам предлагают набор очень острых, очень сильных инструментов, которые могут создать много вещей в руках квалифицированного рабочего и нанести всевозможные метафорические травмы при неправильном обращении. Некоторые из них трудно понять и правильно использовать. И люди, естественно, не склонны к риску, делают иррациональные вещи, такие как проверка указателя на NULL значение перед вызовом free с ним…

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

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


по мере того как вы имеете команду проверки качества на месте, позвольте мне добавить небольшой пункт о КА. Некоторые автоматизированные инструменты QA для C будут помечать назначения освобожденным указателям как " бесполезное назначение ptr". Например, PC-lint/FlexeLint из программного обеспечения Gimpel говорит tst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used

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


всегда рекомендуется объявлять переменную указателя с NULL, например,

int *ptr = NULL;

допустим,ptr указывает 0x1000 адрес в памяти. После использования free(ptr), всегда рекомендуется аннулировать переменную указателя, объявив снова NULL. например:

free(ptr);
ptr = NULL;

если не повторно объявлен NULL переменная указатель продолжает указывать на тот же адрес (0x1000), эта переменная указателя называется оборванных указатель. Если вы определяете другую переменную указателя (скажем,q) и динамически выделять адрес новому указателю, есть шанс взять тот же адрес (0x1000) к новой переменной указателя. Если в случае, вы используете тот же указатель (ptr) и обновите значение по адресу, указанному тем же указателем (ptr), то программа в конечном итоге напишет значение в том месте, где q указывая (с p и q указывают на тот же адрес (0x1000)).

например

*ptr = 20; //Points to 0x1000
free(ptr);
int *q = (int *)malloc(sizeof(int) * 2); //Points to 0x1000
*ptr = 30; //Since ptr and q are pointing to the same address, so the value of the address to which q is pointing would also change.

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

однако, если вы не установите указатель на NULL и по ошибке попробуйте отменить ссылку на указатель или изменить значение этого адреса; вы все равно можете это сделать. НО НЕ ТО, ЧТО ВЫ ЛОГИЧЕСКИ ХОТЕЛИ БЫ СДЕЛАТЬ.

Почему я все еще могу получить доступ к место памяти, которое я освободил? Потому что: возможно, у вас есть свободная память, но переменная указателя все еще имела информацию об адресе памяти кучи. Итак, в качестве оборонительной стратегии, пожалуйста, установите значение NULL.