Должен ли я освободить char *, инициализированный строковыми литералами? - PullRequest
19 голосов
/ 29 февраля 2012

Должен ли я освобождать char* переменные, когда они были инициализированы строковыми литералами? Для меня синтаксис привел бы меня к предположению, что они только размещены в стеке, но этот пример показал мне, что это не так.

#include <stdlib.h>
#include <stdio.h>

static char* globalBuffer;

typedef struct Container {
    char* buffer;
} Container;

Container* Container_new(char* buffer) {
    Container* container = malloc(sizeof(Container));
    container->buffer    = buffer;
    globalBuffer         = buffer;
    return container;
}

void Container_print(Container* container) {
    if (container->buffer != NULL) {
        printf("%s", container->buffer);
        printf("\n");
    }
    else {
        printf("Container contains a NULL-buffer.");
    }
}

Container* stage() {
    Container* container = Container_new("Test-string.");
    Container_print(container);
    return container;
}

int main() {
    Container* container = stage();
    Container_print(container);

    free(container);
    Container_print(container); // I know, this results in undefined behaviour

    printf(globalBuffer);
    printf("\n");

    return 0;
}

Я получаю следующий вывод:

C:\Users\niklas\Desktop>gcc char_test.c

C:\Users\niklas\Desktop>a.exe
Test-string.
Test-string.
­6>
Test-string.

C:\Users\niklas\Desktop>

Итак, char*, инициализированный строковыми литералами, все еще существует, даже если он вышел из области видимости.

Итак, мой вопрос, я должен освободить такие char* указатели? Будет ли это правильно main()?

int main() {
    Container* container = stage();
    Container_print(container);

    free(container->buffer);    // NEW
    free(container);
    Container_print(container);

    printf(globalBuffer);
    printf("\n");

    return 0;
}

Ответы [ 2 ]

31 голосов
/ 29 февраля 2012

Строковые литералы хранятся таким образом, что они доступны на протяжении всей жизни программы; если ты пишешь

char *ptr = "This is a test";

все, что записано в ptr, это адрес строкового литерала "This is a test". Даже если переменная ptr выходит из области видимости, строковый литерал продолжает существовать в своем собственном разделе памяти, который является не тем же разделом, который используется malloc (по крайней мере, не в уровень). Обратите внимание, что несколько экземпляров одного строкового литерала могут разрешаться в одном и том же месте; IOW, учитывая

char *p0 = "This is a test";
char *p1 = "This is a test";

p0 и p1 могут оба содержать один и тот же адрес (компилятору решать, отображаются ли множественные вхождения строковых литералов в одно и то же место или нет).

Когда вы звоните Container_new, все, что вы делаете, это копируете адрес в container->buffer и globalBuffer; оба они указывают на одну и ту же вещь, которая существует независимо от того и другого. free -ing container не влияет на строковый литерал, на который указывает container->buffer, поэтому printf(globalBuffer); по-прежнему отображает "Test-string.".

Таким образом, вы должны не звонить

free(container->buffer);

для этой конкретной программы, поскольку вы не присваивали ей результат вызова malloc, calloc или realloc.

Если, ОТО, вы написали Container_new как

Container* Container_new(char* buffer) 
{
  Container* container = malloc(sizeof(Container));
  container->buffer    = malloc(strlen(buffer) + 1);  // Allocate memory to 
  if (container->buffer)                              // store a *new* instance
  {                                                   // of the input string. 
    strcpy(container->buffer, buffer);                // This will need to be 
  }                                                   // freed before freeing
  globalBuffer         = buffer;                      // the container
  return container;
}

тогда вам потребуется , чтобы освободить container->buffer до освобождения container.

31 голосов
/ 29 февраля 2012

Вы никогда не должны free() память, которую вы не malloc() изд.

Способ, которым компилятор реализует строковые литералы, - это не ваше дело: это детали реализации. Вы можете free() указатель на память, которую вы выделили с помощью malloc(), и только те, или вы рискуете жизнью своей системы.

В идеале malloc() вызовы и free() вызовы должны появляться на одном и том же «уровне проектирования» (например, внутри одного и того же файла реализации для одного и того же модуля), и они должны идеально совпадать: по одному free() для каждого malloc(). но это не всегда возможно.

(Обратите внимание, что некоторая библиотека выделяет блоки памяти, возвращает указатели на эти блоки и дает вам указание освободить их. В этом случае вам разрешено освобождать эти указатели, но это плохая практика проектирования из люди, которые создали библиотеку.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...