Подтвердить что ты не робот

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

4b9b3361

Ответ 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; 

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