Как проверить, указывает ли char * на строковый литерал в C - PullRequest
20 голосов
/ 03 августа 2010

У меня есть структура

struct request {
  int code;
  char *message;
};

, которую я хотел бы освободить должным образом.

У меня есть следующая функция для этого:

void free_request(struct request *req) {
  if (req->message != NULL) {
      free(req->message);
  }
  free(req);
  req = NULL;
}

проблема заключается в том, что я получаю ошибку «free (): неверный указатель» / segfault от компилятора при попытке освободить запрос, созданный с использованием строкового литерала:

struct request *req;
req = malloc(sizeof(struct request));
req->message = "TEST";
free_request(req);

, поскольку я хочуструктура запросов в разных местах, один раз с использованием литералов (на стороне клиента) и один раз с использованием * chars, которые я читаю из сокета (на стороне сервера), мне было интересно, если есть функция, чтобы убедиться, что я не пытаюсьосвободить литералы, но при этом разрешить мне освободить сообщение, созданное мной с помощью malloc.

Ответы [ 6 ]

24 голосов
/ 03 августа 2010

Нет стандартной функции, позволяющей узнать, был ли указатель выделен динамически или нет. Вы должны включить флаг в свою структуру, чтобы сообщить себе об этом, или использовать только динамически размещенные строки (strdup - ваш друг в этом случае). В зависимости от настроек сети, может быть проще использовать strdup (ну, по правде говоря, проще strdup вообще использовать *1005*).

С 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**, либо сделать это самостоятельно после вызова функции.

5 голосов
/ 03 августа 2010

Невозможно определить, используете ли вы строковый литерал (ну, вы можете поместить строковые литералы в пользовательский раздел .se, созданный GCC, а затем проверить указатель строки, чтобы определить, содержится ли он в разделе .sese ofлитералы).Однако ... есть более простой способ использования простого шаблона программирования.

Распределение с помощью литерала

Нормальный случай.Вызов 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);

Редактирование: Общий случай

Обратите внимание, что приведенная выше схема выделения для dynamic string является общей, ее можно использовать, даже если вы не знаете, является ли some_string литералом или нет.Таким образом, единственная функция, которая обрабатывает оба случая и освобождает с помощью free(), освобождает вас от особых случаев.

4 голосов
/ 03 августа 2010

Я бы предложил добавить элемент к struct request, чтобы указать, динамически ли выделяется request :: message, и установить этот элемент одновременно с назначением request::message, а затем проверить его перед освобождением памяти.Это немного запутанно в C.

Обратите внимание, что причиной могут быть не только строковые литералы, любой указатель на данные, которые динамически не распределяются в куче с помощью malloc () или calloc (), завершится неудачей, поэтому простообнаружение ", если символ указывает на строковый литерал в C" *, даже если это можно сделать переносимым, не поможет.

2 голосов
/ 03 августа 2010

Это segfaults, потому что область памяти, содержащая "TEST" (обычно) только для чтения и не находится в куче (обычно потому, что она находится в некотором разделе программы только для чтения).Имея только указатель char*, вы не сможете узнать, указывает ли он на free() -значимую строку или нет.Вместо этого вы должны выделить буфер для req->message и скопировать символы.

char* str = "TEST";
int len = strlen(str);
req->message = malloc(len+1);
strcpy(req->message, str);

Или вы можете использовать strdup(), как предложено zneak .

0 голосов
/ 05 августа 2010

Посмотрите на:

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; 

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

0 голосов
/ 03 августа 2010

Если вы просто пытаетесь убедиться, что память malloc освобождена, вы можете позвонить

realloc(req->message,(size_t)0)

Если реализация библиотеки памяти устойчива, она должна работать.

...