Czy powinienem zwolnić zmienne char*
, gdy zostały zainicjowane za pomocą literałów ciągu? Według mnie składnia doprowadziłaby mnie do założenia, że są one alokowane tylko na stosie, ale ten przykład pokazał mi, że tak nie jest.
#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;
}
Otrzymuję następujący wynik:
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>
Tak więc char*
zainicjowany za pomocą literałów ciągów nadal istnieje, nawet jeśli wyszedł poza zakres.
Więc moje pytanie, czy powinienem zwolnić takie char*
wskaźniki? Czy to byłby właściwy 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 odpowiedzi
Literały łańcuchowe są przechowywane w taki sposób, że są dostępne przez cały okres istnienia programu; jeśli piszesz
char *ptr = "This is a test";
Wszystko, co jest zapisywane do ptr
, to adres literału ciągu znaków "This is a test"
. Nawet jeśli zmienna ptr
wyjdzie poza zakres, literał ciągu nadal istnieje we własnej sekcji pamięci, która nie jest tą samą sekcją używaną przez malloc
(przynajmniej , a nie na poziomie logicznym). Zauważ, że wiele wystąpień tego samego literału łańcuchowego może rozwiązać w tej samej lokalizacji; IOW, biorąc pod uwagę
char *p0 = "This is a test";
char *p1 = "This is a test";
p0
i p1
mogą zawierać ten sam adres (od kompilatora zależy, czy wielokrotne wystąpienia literałów ciągów są mapowane do tej samej lokalizacji, czy nie).
Kiedy dzwonisz Container_new
, wszystko co robisz to kopiowanie adresu do container->buffer
i globalBuffer
; obie kończą się wskazywaniem na tę samą rzecz, która istnieje niezależnie od każdego z nich. free
-ing container
nie wpływa na literał ciągu, na który wskazuje container->buffer
, więc printf(globalBuffer);
nadal wyświetla "Test-string."
.
Podsumowując, nie dzwonić
free(container->buffer);
Dla tego konkretnego programu, ponieważ nie przypisałeś do niego wyniku wywołania malloc
, calloc
ani realloc
.
Jeśli, OTOH, napisałeś Container_new
jako
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;
}
Wtedy musisz uwolnić container->buffer
przed zwolnieniem container
.
Nigdy nie będziesz free()
pamiętał, którego nie malloc()
edytowałeś.
Sposób, w jaki kompilator implementuje literały łańcuchowe, nie jest Twoją sprawą: to szczegół implementacji. Możesz free()
wskazać pamięć, którą przydzieliłeś za pomocą malloc()
i tylko te, w przeciwnym razie ryzykujesz życiem swojego systemu.
Idealnie, wywołania malloc()
i wywołania free()
powinny pojawić się na tym samym „poziomie projektu” (na przykład w tym samym pliku implementacyjnym dla tego samego modułu) i powinny idealnie pasować: jeden free()
dla każdego malloc()
. ale nie zawsze jest to możliwe.
(Zauważ, że niektóre biblioteki przydzielają bloki pamięci, zwracają wskaźniki do tych bloków i instruują Cię, abyś je uwolnił. W tym przypadku możesz uwolnić te wskaźniki, ale jest to zła praktyka projektowa ze strony ludzi, którzy stworzyli biblioteka.)
Podobne pytania
Powiązane pytania
Nowe pytania
c
C jest językiem programowania ogólnego przeznaczenia, używanym do programowania systemów (system operacyjny i systemy wbudowane), bibliotek, gier i wielu platform. Ten znacznik powinien być używany w przypadku ogólnych pytań dotyczących języka C, zgodnie z definicją w standardzie ISO 9899 (najnowsza wersja 9899: 2018, chyba że określono inaczej - również oznaczanie żądań dotyczących wersji za pomocą c89, c99, c11 itd.). C różni się od C ++ i nie należy go łączyć ze znacznikiem C ++ bez racjonalnego powodu.