Проверка с практическими рекомендациями, если символ* указывает на строковый литерал в C

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

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.

20
задан Niklas 3 August 2010 в 16:45
поделиться

6 ответов

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

24
ответ дан 30 November 2019 в 00:13
поделиться

Нет способа определить, используете ли вы строковый литерал (Ну, вы можете поместить строковые литералы в пользовательский .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() избавляет вас от особых случаев.

5
ответ дан 30 November 2019 в 00:13
поделиться

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

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

4
ответ дан 30 November 2019 в 00:13
поделиться

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

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

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

2
ответ дан 30 November 2019 в 00:13
поделиться

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

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

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

0
ответ дан 30 November 2019 в 00:13
поделиться

Взгляните на:

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
ответ дан 30 November 2019 в 00:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: