Większość implementacji funkcji alokacji pamięci C będzie przechowywać informacje rozliczeniowe dla każdego bloku, w linii lub osobno.
Jednym typowym sposobem (w linii) jest faktyczne przydzielenie zarówno nagłówka, jak i pamięci, o którą prosiłeś, uzupełnionych do pewnego minimalnego rozmiaru. Na przykład, jeśli poprosiłeś o 20 bajtów, system może przydzielić blok 48-bajtowy:
- 16-bajtowy nagłówek zawierający rozmiar, specjalny znacznik, sumę kontrolną, wskaźniki do następnego / poprzedniego bloku i tak dalej.
- 32-bajtowy obszar danych (20 bajtów uzupełniono do wielokrotności 16).
Adres podany następnie jest adresem obszaru danych. Następnie, kiedy zwolnisz blok, free
po prostu weźmie podany adres i, zakładając, że nie upchnąłeś tego adresu lub pamięci wokół niego, sprawdź informacje księgowe bezpośrednio przed nim. Graficznie byłoby to następująco:
____ The allocated block ____
/ \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
^
|
+-- The address you are given
Należy pamiętać, że rozmiar nagłówka i wypełnienia są całkowicie zdefiniowane dla implementacji (właściwie cała rzecz jest zdefiniowana dla implementacji (a), ale powszechna jest opcja rozliczania w linii).
Sumy kontrolne i specjalne znaczniki, które istnieją w informacjach księgowych, są często przyczyną błędów, takich jak „uszkodzona arena pamięci” lub „podwójne zwolnienie”, jeśli je zastąpisz lub zwolnisz dwukrotnie.
Wypełnianie (aby zwiększyć efektywność alokacji) sprawia, że czasami możesz pisać trochę poza końcem żądanego miejsca bez powodowania problemów (nadal nie rób tego, jest to niezdefiniowane zachowanie i tylko dlatego, że czasami działa, nie robi tego oznacza to, że można to zrobić).
(a) Napisałem implementacje malloc
systemów wbudowanych, w których masz 128 bajtów bez względu na to, o co prosiłeś (był to rozmiar największej struktury w systemie), zakładając, że poprosiłeś o 128 bajtów lub mniej (prośby o więcej byłyby zostać spełniony z wartością NULL zwracaną). Bardzo prosta maska bitowa (tj. Nie w linii) została użyta do podjęcia decyzji, czy porcja 128-bajtowa została przydzielona, czy nie.
Inne, które opracowałem, miały różne pule dla fragmentów 16-bajtowych, fragmentów 64-bajtowych, fragmentów 256-bajtowych i 1K, ponownie za pomocą maski bitowej, aby zdecydować, które bloki zostały użyte lub dostępne.
Obie te opcje pozwoliły zmniejszyć obciążenie informacji księgowych oraz zwiększyć szybkość malloc
i free
(nie trzeba łączyć sąsiednich bloków podczas uwalniania), szczególnie ważne w środowisku, w którym pracowaliśmy.