Как проверить, указывает ли char * на строковый литерал в C
У меня есть структура
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.
Ответы
Ответ 1
Нет стандартной функции, которая позволяет узнать, был ли указатель динамически распределен или нет. Вы должны включить флаг в свою структуру, чтобы сообщить об этом, или использовать только динамически выделенные строки (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**
, либо сделать это самостоятельно после вызова функций.
Ответ 2
Невозможно определить, используете ли вы строковый литерал (ну, вы можете поместить строковые литералы в пользовательскую. секцию, созданную 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()
, позволяет вам делать особые случаи.
Ответ 3
Я бы предложил добавить член в struct request
, чтобы указать, будет ли запрос: сообщение динамически распределено, и установите этот член в то же самое время, когда вы назначаете request::message
, а затем проверьте его, прежде чем освобождать память. Это немного грязно в C.
Обратите внимание, что это вызовет не только строковые литералы, но и любой указатель на данные, не динамически распределенные в куче malloc() или calloc(), не будут работать, поэтому просто обнаруживает ", если char указывает на строковый литерал в C" *, даже если это можно сделать портативно, не помогло бы.
Ответ 4
Это segfault, потому что ячейка памяти, содержащая "TEST"
, является (обычно) доступной только для чтения и не находится в куче (обычно из-за того, что она находится в некоторой части только для чтения программы). Учитывая только указатель char*
, вы не сможете узнать, указывает ли он на строку free()
-able или нет. Вместо этого вы должны выделить буфер для req->message
и скопировать символы.
char* str = "TEST";
int len = strlen(str);
req->message = malloc(len+1);
strcpy(req->message, str);
Или вы можете использовать strdup()
, как было предложено zneak.
Ответ 5
Если вы просто пытаетесь убедиться, что память malloc'ed освобождена, вы можете вызвать
realloc(req->message,(size_t)0)
Если реализация библиотеки памяти является надежной, она должна работать.
Ответ 6
Посмотрите:
struct request *req;
req = calloc(1,sizeof(struct request));
strcpy(req->message = malloc(strlen("TEST")+1),"TEST");
free_request(req);
Соответствует строго ANSI C.
strdup isnt ANSI C.
req = NULL;
является избыточным.