C двойная бесплатная ошибка после 2-го запуска кода

#c #scope #segmentation-fault #function-definition #null-pointer

#c #масштаб #ошибка сегментации #функция-определение #нуль-указатель

Вопрос:

Я пытаюсь разработать библиотеку на Camp;, начиная с управления памятью.

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

Тем не менее, при 2-м запуске бесплатной функции я получаю двойной бесплатный и сбой.

Мой простой заголовок:

 #include lt;stdio.hgt; #include lt;stdlib.hgt; #include "xyz_props.h" #include lt;assert.hgt; #include lt;string.hgt;   Header:  #ifndef XYZ_PROPS_H #define XYZ_PROPS_H  #ifdef __cplusplus extern "C" { #endif #include lt;stdlib.hgt; #define DEFAULT_MAX_KEY_SIZE_IN_BYTES 256 #define DEFAULT_MAX_VALUE_SIZE_IN_BYTES 4096   /*represents a single linked list node with (key,value) pair*/ typedef struct xyz_config_node {  char* key;   char* value;   struct xyz_config_node* p_next;    } xyz_config_node;  /*represents all properties*/ typedef struct xyz_config_list {  xyz_config_node* p_head;  int KEY_SIZE_IN_BYTES;   int VALUE_SIZE_IN_BYTES;    } xyz_config_list;  /*declare variables*/ extern xyz_config_list* p_self; /*=========================================================================== Function: xyz_config_alloc  Description: allocates heap memory for the wrapper xyz_config_list   * that contains the head node. Inputs: max_key_size in bytes.   * Input of 0 or greater than 4096 will default to 256 bytes   * length for the key size.   *   * max_value_size in bytes.   * Input of 0 or greater than 4096 will default to 4096 bytes   * length for the value size.  Outputs: pointer to xyz_config_list ==========================================================*/ xyz_config_list* xyz_config_alloc(int max_key_size, int max_value_size);  /*=========================================================================== Function: xyz_config_free Description: Frees heap memory allocated to xyz_config_list amp; the  * linked list within xyz_config_list.  Inputs: xyz_config_list** pp_self - pass by reference  Outputs: void References:  Example call: xyz_config_free(amp;props);  ==========================================================*/ void xyz_config_free(xyz_config_list** pp_self);  

Файл реализации C:

 #include lt;stdio.hgt; #include lt;stdlib.hgt; #include lt;stdbool.hgt; #include lt;string.hgt; #include "xyz_props.h"  xyz_config_list* p_self = NULL;  /*============================================================================= Private Declarations ==============================================================================*/ xyz_config_list* xyz_config_alloc_helper(int max_key_size, int max_value_size, xyz_config_list** props);  /*=============================================================================  * Implementations ==============================================================================*/ xyz_config_list* xyz_config_alloc(int max_key_size, int max_value_size) {  return xyz_config_alloc_helper(max_key_size, max_value_size,amp;p_self); }  xyz_config_list* xyz_config_alloc_helper(int max_key_size, int max_value_size, xyz_config_list** props) {  if (NULL == *props) {  *props = (xyz_config_list*) calloc(1, sizeof(xyz_config_list));  //set max key size  if (max_key_size gt; 0 amp;amp; max_key_sizelt;=4096) {  (*props)-gt;KEY_SIZE_IN_BYTES = max_key_size;  } else {  //defaults to 256   (*props)-gt;KEY_SIZE_IN_BYTES = DEFAULT_MAX_KEY_SIZE_IN_BYTES;  fprintf(stderr,"WARNING xyz_config,xyz_config_alloc_helper(), "  "max_key_size MUST be 0lt;max_key_sizelt;=4096.max_key_size is set to "  "default 256.n");  }  //set max value size  if (max_value_size gt; 0 amp;amp; max_value_sizelt;=4096) {  (*props)-gt;VALUE_SIZE_IN_BYTES = max_value_size;  } else {  //defaults to 4096  (*props)-gt;VALUE_SIZE_IN_BYTES = DEFAULT_MAX_VALUE_SIZE_IN_BYTES;  fprintf(stderr,"WARNING xyz_config,xyz_config_alloc_helper(), "  "max_value_size MUST be 0lt;max_value_sizelt;=4096.max_value_size is set to "  "default 4096.n");  }  }  return *props; }  void xyz_config_free(xyz_config_list** pp_self) {  if (NULL!=pp_self amp;amp; NULL!=(*pp_self))  {  xyz_config_node* p_current = (*pp_self)-gt;p_head;   xyz_config_node* p_next = NULL;   //iterate and free the nodes   while (NULL!=p_current)  {  p_next = p_current-gt;p_next;  //free child attributes   free(p_current-gt;key);  free(p_current-gt;value);  //free the node   free(p_current);  p_current = p_next;     }  //free the super structure  if (NULL!=*pp_self)  {  free (*pp_self); //ERROR HAPPENS ON 2ND TIME HERE.  *pp_self = NULL;   }   } }  

Main file:

 /*  *   */ void test(); void test2(); int main(int argc, char** argv) {   test();  return (EXIT_SUCCESS); }  /*single alloc amp; free*/ void test() {   xyz_config_list* props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096  assert(props);   //finally free all memory   xyz_config_free(amp;props);  assert(NULL==props);    printf("freen"); }  /*multiple allocs amp; frees*/ void test2() {  //1-alloc  xyz_config_list* props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096  assert(props);   //1-finally free all memory   xyz_config_free(amp;props);  assert(NULL==props);    //2- alloc   props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096  assert(props);   //2-finally free all memory   xyz_config_free(amp;props); //CRASH in 2nd free function. Output: RUN FINISHED; Segmentation fault; core dumped;   assert(NULL==props);    printf("freen"); }  

Память и переменные отладчика наблюдают за 1-м запуском:

введите описание изображения здесь

Память и переменные отладчика наблюдают за 2-м запуском, когда возникает проблема:

введите описание изображения здесь

Любая помощь будет оценена по достоинству.

Комментарии:

1. Почему у вас вообще есть глобальная p_self переменная? Я думаю, что было бы намного проще отлаживать, если бы вы избавились от этого.

2. @EdmCoff, ну, это просто место, где живет переменная, это просто указатель на структуру, которая содержит как другие структуры данных, так и другие соответствующие данные. Без ООП было бы трудно поступить иначе, независимо от того, что это напрямую не связано с double free. Какую модель вы бы предложили?

3. @EdmCoff, хорошо, я удалил extern ключевое слово. Это была реликвия из ранней версии.

Ответ №1:

Похоже, проблема связана с объявлением переменной p_self в области файла

 xyz_config_list* p_self = NULL;  

Эта переменная используется в функции xyz_config_alloc_helper . Память выделяется только в том случае, если переменная области действия файла p_self равна NULL .

 xyz_config_list* xyz_config_alloc(int max_key_size, int max_value_size) {  return xyz_config_alloc_helper(max_key_size, max_value_size,amp;p_self);  ^^^^^^^ }  

и (в рамках функции xyz_config_alloc_helper )

 if (NULL == *props) {  *props = (xyz_config_list*) calloc(1, sizeof(xyz_config_list));  

Однако в функции xyz_config_free переменная не задана NULL , поскольку функция имеет дело с локальной переменной, объявленной в функциях test и test2, например

 xyz_config_list* props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096 assert(props);  //1-finally free all memory  xyz_config_free(amp;props);  

А вот фрагмент кода из функции, где он устанавливает NULL локальный указатель, переданный функции по ссылке

 if (NULL!=*pp_self)  {  free (*pp_self); //ERROR HAPPENS ON 2ND TIME HERE.  *pp_self = NULL;   }  

То есть *pp_self не является тем же объектом, что и объект в области файла p_self .

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

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

Комментарии:

1. Урок усвоен. Однако, как бы я избежал передачи p_self при каждом вызове, если бы я не объявлял его глобально???

2. @cyber101 Что мешает передавать локальные указатели по ссылке на функцию xyz_config_alloc? В противном случае вам необходимо установить переменную области действия файла в значение NULL в функции, которая освобождает выделенную память

3. Верно, однако предоставление функций, требующих ссылки на эту структуру, выглядит уродливо для конечного пользователя. Я просто хотел что-то вроде этого добавить(ключ,значение), а не добавлять(ключ,значение и pp_self) в заголовок.

4. @cyber101 К сожалению, я ничего не могу сказать, потому что я не знаю дизайнерских идей программы. Я указал причину ошибки во время выполнения.