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