У меня есть структура
struct request { int code; char *message; };
что я хотел бы освободиться должным образом.
У меня есть следующая функция:
void free_request(struct request *req) { if (req->message != NULL) { free(req->message); } free(req); req = NULL; }
Проблема в том, что я получаю сообщение «free (): invalid pointer» / segfault от компилятора, когда я пытаюсь освободить запрос, который был создан с использованием строкового литерала:
struct request *req; req = malloc(sizeof(struct request)); req->message = "TEST"; free_request(req);
Поскольку я хочу создавать структуры запроса в разных местах, однажды используя литералы (на стороне клиента) и однажды используя * символы, которые я прочитал из сокета (на стороне сервера), мне было интересно, есть ли функция, чтобы убедиться, что я не пытайтесь освободить литералы, но все же разрешите мне освободить сообщение, которое я создал, используя malloc.
Нет стандартной функции, которая позволяет узнать, был ли указатель динамически распределен или нет. Вы должны включить флаг в свою структуру, чтобы проинформировать об этом, или использовать только динамически выделенные строки (в этом случае strdup
– ваш друг). В зависимости от вашей сетевой настройки, может быть проще использовать strdup
(ну, честно говоря, проще всего использовать strdup
).
С strdup
:
struct message* req; req = malloc(sizeof *req); req->message = strdup("TEST"); free_request(req);
С флагом:
struct message { int code; char* message; bool isStatic; // replace to 'char' if bool doesn't exist }; void free_request(struct message* req) { if (!req->isStatic) free(req->message); free(req); } struct message* req; req = malloc(sizeof *req); req->message = "TEST"; req->isStatic = 1; free_request(req);
Кроме того, не забудьте обнулить выделенную память при создании объекта. Это может сэкономить вам массу неприятностей.
req = malloc(sizeof *req); memset(req, 0, sizeof *req);
Это, и установка req
в NULL
из free_request
не будет иметь никакого эффекта. Вы либо должны взять struct message**
или сделать это самостоятельно после вызова функции.
Невозможно определить, используете ли вы строковый литерал (ну, вы можете поместить строковые литералы в пользовательскую. Секцию, созданную GCC, а затем изучить указатель строки, чтобы определить, содержится ли она в разделе. Литералов). Однако … есть лучший способ использовать простой шаблон программирования.
Обычный случай. Звонок на бесплатный (req) будет работать так, как ожидалось: освобождение структуры запроса.
struct *req; req = malloc(sizeof(*req)); req->message = "TEST";
В следующем случае some_string
– это строка, которую вы хотите сохранить в качестве сообщения запроса. Это может быть либо буквальный, либо динамически распределенный. Это выделяет память для строки, когда сама структура выделяется (и будет автоматически освобождена при освобождении структуры).
struct *req; req = malloc(sizeof(*req)+strlen(some_string)+1); req->message = (char *)&req[1]; strcpy(req->message, some_string);
free(req);
Обратите внимание, что схема распределения выше для dynamic string
является общей, ее можно использовать, даже если вы не знаете, является ли some_string
литералом или нет. Таким образом, одна функция, которая заботится обо всех случаях, и освобождая с помощью free()
избавляет вас от особых случаев.
Я бы предложил добавить член в struct request
чтобы указать, будет ли запрос: сообщение динамически распределено, и установите этот член в то же время, когда вы назначаете request::message
, а затем проверяйте его перед освобождением памяти. Это немного грязно в C.
Обратите внимание, что это вызовет не только строковые литералы, но и любой указатель на данные, не динамически распределенные по куче malloc () или calloc (), не будут работать, поэтому просто обнаружение «если символ указывает на строковый литерал в C», * Даже если это можно сделать портативно, это не поможет.
Это segfaults, потому что ячейка памяти, содержащая "TEST"
обычно (как правило) доступна только для чтения и не находится в куче (обычно из-за того, что она находится в некоторой части только для чтения программы). Учитывая только указатель char*
, вы не сможете узнать, указывает ли он на free()
строку или нет. Вместо этого вы должны выделить буфер для req->message
и скопировать символы.
char* str = "TEST"; int len = strlen(str); req->message = malloc(len+1); strcpy(req->message, str);
Или вы можете использовать strdup()
как было предложено zneak .
Если вы просто пытаетесь убедиться, что память malloc’ed освобождена, вы можете позвонить
realloc(req->message,(size_t)0)
Если реализация библиотеки памяти является надежной, она должна работать.
Посмотри на:
struct request *req; req = calloc(1,sizeof(struct request)); strcpy(req->message = malloc(strlen("TEST")+1),"TEST"); free_request(req);
Его строго ANSI C соответствует. strdup не ANSI C.
req = NULL;
является избыточным.