Должен ли я освободить 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;
}
Ответы
Ответ 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()
. но это не всегда возможно.
(Обратите внимание, что некоторая библиотека выделяет блоки памяти, возвращает указатели на эти блоки и инструктирует их освобождать их. В этом случае вам разрешено освобождать эти указатели, но это плохая практика проектирования от людей, которые создали в библиотеке.)