Rozumiem, jak działa malloc (). Moje pytanie brzmi: zobaczę takie rzeczy:
#define A_MEGABYTE (1024 * 1024)
char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);
Ze względu na zwięzłość pominąłem sprawdzanie błędów. Moje pytanie brzmi: czy nie możesz po prostu zrobić powyższego, inicjując wskaźnik do jakiejś statycznej pamięci w pamięci? być może:
char *some_memory = "Hello World";
W którym momencie faktycznie musisz samodzielnie przydzielić pamięć zamiast deklarować / inicjować wartości, które chcesz zachować?
c
memory
memory-management
randombity
źródło
źródło
malloc()
może zawieść!Odpowiedzi:
char *some_memory = "Hello World";
tworzy wskaźnik do stałej łańcuchowej. Oznacza to, że ciąg „Hello World” będzie znajdował się gdzieś w części pamięci przeznaczonej tylko do odczytu, a Ty masz do niego po prostu wskaźnik. Możesz użyć ciągu jako tylko do odczytu. Nie możesz wprowadzać w nim zmian. Przykład:
some_memory[0] = 'h';
Prosi o kłopoty.
Z drugiej strony
some_memory = (char *)malloc(size_to_allocate);
alokuje tablicę char (zmienną) i some_memory punktów do tej przydzielonej pamięci. Teraz ta tablica jest zarówno do odczytu, jak i do zapisu. Możesz teraz:
some_memory[0] = 'h';
a zawartość tablicy zmieni się na „hello World”
źródło
const char *s = "hi";
Czy nie jest to faktycznie wymagane przez standard?const char const* s;
W tym dokładnym przykładzie malloc jest mało przydatny.
Głównym powodem, dla którego Malloc jest potrzebny, jest to, że masz dane, które muszą mieć okres istnienia inny niż zakres kodu. Twój kod wywołuje malloc w jednej procedurze, przechowuje gdzieś wskaźnik i ostatecznie wywołuje free w innej procedurze.
Drugim powodem jest to, że C nie ma możliwości sprawdzenia, czy na stosie zostało wystarczająco dużo miejsca na alokację. Jeśli twój kod musi być w 100% niezawodny, bezpieczniej jest użyć malloc, ponieważ wtedy twój kod może rozpoznać błąd alokacji i obsłużyć go.
źródło
malloc to wspaniałe narzędzie do alokacji, ponownego przydzielania i zwalniania pamięci w czasie wykonywania, w porównaniu do statycznych deklaracji, takich jak przykład Hello world, które są przetwarzane w czasie kompilacji i dlatego nie można ich zmienić.
Malloc jest zatem zawsze przydatny, gdy masz do czynienia z danymi o dowolnej wielkości, takimi jak czytanie zawartości plików lub obsługa gniazd, a nie jesteś świadomy długości danych do przetworzenia.
Oczywiście w trywialnym przykładzie, takim jak ten, który podałeś, malloc nie jest magicznym „odpowiednim narzędziem do właściwego zadania”, ale w bardziej złożonych przypadkach (na przykład tworzenie tablicy o dowolnym rozmiarze w czasie wykonywania) jest to jedyny sposób, aby udać się.
źródło
Jeśli nie znasz dokładnego rozmiaru pamięci, której potrzebujesz, potrzebujesz alokacji dynamicznej (
malloc
). Przykładem może być sytuacja, gdy użytkownik otwiera plik w Twojej aplikacji. Będziesz musiał wczytać zawartość pliku do pamięci, ale oczywiście nie znasz z góry rozmiaru pliku, ponieważ użytkownik wybiera plik na miejscu, w czasie wykonywania. Zasadniczo potrzebujesz tego,malloc
gdy nie znasz z góry rozmiaru danych, z którymi pracujesz. Przynajmniej jest to jeden z głównych powodów używaniamalloc
. W twoim przykładzie z prostym ciągiem znaków, którego rozmiar już znasz w czasie kompilacji (a ponadto nie chcesz go modyfikować), nie ma sensu dynamiczne przydzielanie tego.Trochę nie na temat, ale ... musisz bardzo uważać, aby nie spowodować wycieków pamięci podczas używania
malloc
. Rozważ ten kod:int do_something() { uint8_t* someMemory = (uint8_t*)malloc(1024); // Do some stuff if ( /* some error occured */ ) return -1; // Do some other stuff free(someMemory); return result; }
Czy widzisz, co jest nie tak z tym kodem? Istnieje warunkowa instrukcja powrotu między
malloc
afree
. Na początku może się to wydawać w porządku, ale pomyśl o tym. Jeśli wystąpi błąd, wrócisz bez zwalniania przydzielonej pamięci. Jest to częste źródło wycieków pamięci.Oczywiście jest to bardzo prosty przykład i bardzo łatwo jest tu dostrzec błąd, ale wyobraź sobie setki wierszy kodu zaśmieconych wskaźnikami,
malloc
s,free
s i wszelkiego rodzaju obsługą błędów. Rzeczy mogą się bardzo szybko pogmatwać. Jest to jeden z powodów, dla których zdecydowanie wolę nowoczesny C ++ od C w odpowiednich przypadkach, ale to zupełnie inny temat.Więc kiedy używasz
malloc
, zawsze upewnij się, że twoja pamięć jestfree
jak największa.źródło
char *some_memory = "Hello World"; sprintf(some_memory, "Goodbye...");
jest niedozwolone, literały łańcuchowe są
const
.Spowoduje to przydzielenie 12-bajtowej tablicy znaków na stosie lub globalnie (w zależności od tego, gdzie jest zadeklarowana).
char some_memory[] = "Hello World";
Jeśli chcesz zostawić miejsce na dalsze manipulacje, możesz określić, że rozmiar tablicy powinien być większy. (Proszę jednak nie umieszczać 1 MB na stosie.)
#define LINE_LEN 80 char some_memory[LINE_LEN] = "Hello World"; strcpy(some_memory, "Goodbye, sad world..."); printf("%s\n", some_memory);
źródło
Jednym z powodów, dla których konieczne jest przydzielenie pamięci, jest to, że chcesz ją zmodyfikować w czasie wykonywania. W takim przypadku można użyć malloc lub bufora na stosie. Prosty przykład przypisania „Hello World” do wskaźnika definiuje pamięć, której „zazwyczaj” nie można modyfikować w czasie wykonywania.
źródło