Intereting Posts

Как проверить, если символ * указывает на строковый литерал в 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.

Нет стандартной функции, которая позволяет узнать, был ли указатель динамически распределен или нет. Вы должны включить флаг в свою структуру, чтобы проинформировать об этом, или использовать только динамически выделенные строки (в этом случае 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; 

является избыточным.