У меня есть структура
struct request {
int code;
char *message;
};
то, что я хотел бы освободить правильно.
У меня есть следующая функция, чтобы сделать это:
void free_request(struct request *req) {
if (req->message != NULL) {
free(req->message);
}
free(req);
req = NULL;
}
Проблема состоит в том, что я становлюсь "свободным (): недопустимый указатель"/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
не будут иметь никакого эффекта. Вам нужно либо принять сообщение структуры **
, либо сделать это самостоятельно после вызова функции.
Нет способа определить, используете ли вы строковый литерал (Ну, вы можете поместить строковые литералы в пользовательский .section, созданный GCC, а затем исследовать указатель строки, чтобы определить, содержится ли он в .section литералов). Однако... есть лучший способ, использующий простую схему программирования.
Обычный случай. Вызов free(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);
Обратите внимание, что приведенная выше схема выделения для динамической строки
является общей, ее можно использовать даже тогда, когда вы не знаете, является ли some_string
литералом или нет. Таким образом, одна функция, которая заботится об обоих случаях, и освобождение с помощью free()
избавляет вас от особых случаев.
Я бы предложил добавить член в struct request
, чтобы указать, выделяется ли request :: message динамически, и установить этот член одновременно с назначением запроса :: message
, затем проверьте его перед освобождением памяти. Это немного беспорядочно в C.
Обратите внимание, что проблема может быть вызвана не только строковыми литералами, любой указатель на данные, которые не выделяются динамически в куче с помощью malloc () или calloc (), не сработает, поэтому простое обнаружение ] "если char указывает на строковый литерал в C" * , даже если это можно сделать переносимо, не поможет.
Сбой, потому что область памяти, содержащая «ТЕСТ»
, (обычно) предназначена только для чтения и не находится в куче (обычно потому, что она находится в некотором разделе, доступном только для чтения. программы). Имея только указатель char *
, вы не сможете узнать, указывает ли он на строку, доступную для free ()
, или нет. Вместо этого вы должны выделить буфер для req-> message
и скопировать символы.
char* str = "TEST";
int len = strlen(str);
req->message = malloc(len+1);
strcpy(req->message, str);
Или вы можете использовать strdup ()
, как предлагает zneak .
Если вы просто пытаетесь убедиться, что выделенная память освобождена, вы можете вызвать
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;
является избыточным.