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

Должен ли я освободить char *, инициализированный с использованием строковых литералов?

Должен ли я освобождать переменные 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(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;
}
4b9b3361

Ответ 1

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

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.

Если, OTOH, вы написали 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.

Ответ 2

Вы никогда не должны free() памяти, что вы не malloc() ed.

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

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

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