LINUX.ORG.RU

Как гарантированно заNULLить указатель на уже освобожденный объект

 , , ,


0

3

Всех приветствую!

Может кому-нибудь мой вопрос покажется надуманным, но все равно напишу.

Есть (в данном примере игрушечный) код

KIT*   kit=NULL;

// создаем kit
kit = kit_create("Abracadabra", 5);

// пользуемся kit
// ...
kit_print(stdout, kit);
// ...

// освобождаем kit
kit_free(kit);
	
// ...	

// забыли, что уже освободили kit и делаем распечатку
kit_print(stdout, kit);
//Segmentation fault

Segmentation fault

Т.к. после

#define SAFE_FREE(object) if(object!=NULL) {free(object); (object)=NULL;}
void  kit_free(KIT *kit)
{   
	SAFE_FREE(kit->name);
	SAFE_FREE(kit->data);
	SAFE_FREE(kit);
}

указатель на kit не будет NULL на вызывающей стороне. И даже если сделать проверку в kit_print

if (kit != NULL && kit->data != NULL)
{  
  // печатаем kit
}
else
  printf("Извините, но kit=NULL");

все равно в kit_print придет ненулевой указатель.

Варианты решения:

   kit_free(kit);
   kit=NULL;

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

Сделать

KIT*  kit_free(KIT *kit)
{   
	SAFE_FREE(kit->name);
	SAFE_FREE(kit->data);
	SAFE_FREE(kit);
	return kit; // уже ставший NULL
}

и вызывать

kit = kit_free(kit);

Выглядит неплохо.

А теперь вопрос:

Имеются ли у кого-нибудь более изящные решения или возможен иной принципиально подход к освобождению памяти в pure C ?


Ответ на: комментарий от ttyr

Я вот одно никак понять не могу: как можно забыть о том, что мы уже освободили память и с ней нельзя работать? Это какого размера должна быть функция, чтобы в ней такие несуразицы возникали?

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

К слову, если код вашей функции не умещается в 25-50 строчек, то лучше программистом совсем не работать, не надо нормальных людей своим говнокодом травмировать.

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

//Click on animals with hats only - ну и шиза эта ваша капча =)))

anonymous
()
Ответ на: комментарий от mittorn

В каких-то случаях компилятор твои =NULL просто выкинет

Только если от них нет побочного действия помимо передачи во free

Обращение к освобождённой памяти UB, так что никаких гарантий нет.

anonymous
()
Ответ на: комментарий от Gyros

Я в C не умею. Это просто ещё одна проверка в функции или подмена функции на другую или вовсе на ту которая ничего не делает. Заглушка в моём представлении это код который ничего не выполняет, по типу kit_print() {}; unset kit на псевдо sh.

ext4
()
Последнее исправление: ext4 (всего исправлений: 5)

kit_free сделать макросом, который может занулить переменную. Как SAFE_FREE.

Но проблема, что указатель может быть сохранён в другие переменные и это невозможно отследить. В С предполагается, что программист внимательно следит за указателями и не допускает таких ошибок. Если хочется автоматики, то либо C++ с умными указателями, либо Rust.

KivApple ★★★★★
()

Просто аккуратно пиши код и всё.

А так - ну можешь передавать указатель на указатель, типа

void kit_free(KIT **kit) {
  free((*kit)->name);
  (*kit)->name = NULL;
  free((*kit)->data);
  (*kit)->data = NULL;
  *kit = NULL;
}

Но вообще на С так не пишут. Это ненужные действия, которые замедляют программу.

vbr ★★★★
()