Установка переменной в NULL после освобождения - PullRequest
141 голосов
/ 22 июня 2009

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

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

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

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

Ответы [ 23 ]

4 голосов
/ 22 июня 2009

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

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

4 голосов
/ 10 декабря 2009

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

void free(void *ptr);

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

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

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}
3 голосов
/ 22 июня 2009

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

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

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

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

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

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

2 голосов
/ 07 сентября 2011

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

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

2 голосов
/ 10 декабря 2009

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

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

Написано RageZ в виде дубликата вопроса .

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

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 22 июня 2009

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


if(ptr)
   ptr->CallSomeMethod();

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

2 голосов
/ 10 декабря 2009

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

2 голосов
/ 27 октября 2011

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

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

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

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

2 голосов
/ 22 июня 2009

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

2 голосов
/ 10 декабря 2009

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

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

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

...