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

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

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

#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... while, как предложено ниже, спасибо.

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

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

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

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

void free(void *ptr);

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

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

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

Ответ 11

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

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

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

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

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

Ответ 12

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

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

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

поэтому всегда ходите за

free(ptr);
ptr = NULL;

Ответ 13

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

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

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

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

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

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

Ответ 14

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


if(ptr)
   ptr->CallSomeMethod();

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

Ответ 15

Это может быть скорее аргумент для инициализации всех указателей на 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 может перезаписать все виды несвязанных вещей и привести к уродливым ошибкам. Особенно, если компилятор инициализирует локальные переменные с нулевым значением в режиме отладки, но не один раз оптимизация включена. Таким образом, сборки отладки не показывают никаких признаков ошибки, в то время как сборки выпуска взрываются случайным образом...

Ответ 16

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

Ответ 17

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

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

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

Ответ 18

Идея состоит в том, что если вы попытаетесь разыменовать no-long-valid pointer после ее освобождения, вы захотите провалиться (segfault), а не тихо и таинственно.

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

Ответ 19

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

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

Написано RageZ в повторяющемся вопросе.

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

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

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

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

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

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

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

Написано Мартин против Лёвиса в другом ответе.

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

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

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

Ответ 20

Поскольку у вас есть команда по обеспечению качества на месте, позвольте мне добавить незначительный пункт о 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

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

Ответ 21

К оригинальному вопросу: Установка указателя на NULL непосредственно после освобождения содержимого - полная трата времени, при условии, что код соответствует всем требованиям, полностью отлажен и больше не будет изменен. С другой стороны, защита NULLing, которая была освобождена, может быть весьма полезна, когда кто-то бездумно добавляет новый блок кода под free(), когда дизайн исходного модуля неверен, а в случае его -компилирует-но-не-делать-что-я-хочу ошибок.

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

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

Ответ 22

Всегда рекомендуется объявлять переменную-указатель с 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.

Ответ 23

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

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

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