W programowaniu C możesz przekazać dowolny wskaźnik, który ci się podoba, jako argument do zwolnienia, skąd on zna wielkość przydzielonej pamięci do zwolnienia? Ilekroć przekazuję wskaźnik do jakiejś funkcji, muszę również przekazać rozmiar (tj. Tablica 10 elementów musi otrzymać 10 jako parametr, aby znać rozmiar tablicy), ale nie muszę przekazywać rozmiaru do darmowa funkcja. Dlaczego nie i czy mogę korzystać z tej samej techniki we własnych funkcjach, aby uchronić się przed koniecznością korzystania z dodatkowej zmiennej długości tablicy?
384
Odpowiedzi:
Kiedy dzwonisz
malloc()
, określasz ilość pamięci do przydzielenia. Rzeczywista ilość używanej pamięci jest nieco większa i zawiera dodatkowe informacje, które rejestrują (przynajmniej), jak duży jest blok. Nie możesz (wiarygodnie) uzyskać dostępu do tych innych informacji - i nie powinieneś :-).Kiedy dzwonisz
free()
, po prostu patrzy na dodatkowe informacje, aby dowiedzieć się, jak duży jest blok.źródło
malloc_size()
niezawodnie uzyskiwać dostęp do rozmiaru bloku z poziomumalloc()
wskaźnika ed. Ale nie ma niezawodnego, przenośnego sposobu.free()
wymagał od programisty dokładnego zgłoszenia wielkościmalloc()
bloku? Wycieki pamięci są wystarczająco złe.malloc()
ifree()
, ale musisz przechowywać rozmiar tablicy? Dlaczego nie pozwolą zrobić czegoś takiego,blockSize(ptr)
jeśli i tak przechowują informacje?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:
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: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
ifree
(nie trzeba łączyć sąsiednich bloków podczas uwalniania), szczególnie ważne w środowisku, w którym pracowaliśmy.źródło
malloc
jest to, że w przypadku udanego przypadku otrzymasz blok pamięci co najmniej tak duży, jak to, o co prosiłeś. Poszczególne bloki są ciągłe pod względem sposobu uzyskiwania dostępu do elementów w nich zawartych, ale nie ma wymogu, aby areny, z których pochodzą bloki, były ciągłe.void *x = malloc(200); free(x, 500);
jest nie skończy się dobrze :-) W każdym razie, dla skuteczności, rzeczywista wielkość bufora może być większy (po prostu nie można powoływać się na ten temat).Z
comp.lang.c
listy najczęściej zadawanych pytań: Skąd darmo wie, ile bajtów do uwolnienia?Implementacja malloc / free zapamiętuje rozmiar każdego bloku, ponieważ jest on przydzielany, więc nie trzeba przypominać mu o wielkości podczas zwalniania. (Zazwyczaj rozmiar jest zapisywany w sąsiedztwie przydzielonego bloku, dlatego rzeczy zwykle się psują, jeśli granice przydzielonego bloku są nawet nieco przekroczone)
źródło
free
. Być może odpowiedź nie jest dla ciebie satysfakcjonująca, ale nie sądzę, abyś dostał taką zTa odpowiedź została przeniesiona z Skąd free () wie, ile pamięci należy zwolnić? gdzie najwidoczniej nie udało mi się odpowiedzieć na pozornie zdublowane pytanie. Ta odpowiedź powinna zatem odnosić się do tego duplikatu:
W przypadku
malloc
alokatora sterty przechowuje mapowanie oryginalnego zwróconego wskaźnika na odpowiednie szczegóły potrzebne dofree
późniejszego wczytania pamięci. Zazwyczaj obejmuje to przechowywanie wielkości obszaru pamięci w dowolnej formie odpowiedniej dla używanego alokatora, na przykład rozmiaru surowego lub węzła w drzewie binarnym używanym do śledzenia przydziałów lub liczby używanych „jednostek” pamięci.free
nie zawiedzie, jeśli „zmienisz nazwę” wskaźnika lub powielisz go w jakikolwiek sposób. Nie jest to jednak liczone odniesienie i tylko pierwszyfree
będzie poprawny. Dodatkowefree
s to błędy „podwójnego bezpłatnego”.Próba
free
dowolnego wskaźnika o wartości innej niż zwracana przez poprzedniemalloc
s, a jak dotąd niewyleczona, jest błędem. Nie można częściowo zwolnić regionów pamięci zwróconychmalloc
.źródło
W powiązanej notatce Biblioteka GLib ma funkcje alokacji pamięci, które nie zapisują niejawnego rozmiaru - a następnie przekazujesz parametr size, aby zwolnić. Może to wyeliminować część kosztów ogólnych.
źródło
malloc()
ifree()
są zależne od systemu / kompilatora, więc trudno jest podać konkretną odpowiedź.Więcej informacji na temat tego drugiego pytania .
źródło
Menedżer sterty przechowywał gdzieś pamięć należącą do przydzielonego bloku, gdy została wywołana
malloc
.Sam go nigdy nie wdrożyłem, ale myślę, że pamięć bezpośrednio przed przydzielonym blokiem może zawierać meta informacje.
źródło
Oryginalną techniką było przydzielenie nieco większego bloku i zapisanie rozmiaru na początku, a następnie przekazanie aplikacji pozostałej części bloga. Dodatkowa przestrzeń zawiera rozmiar i ewentualnie linki do połączenia wolnych bloków w celu ponownego użycia.
Istnieją jednak pewne problemy z tymi sztuczkami, takie jak słaba pamięć podręczna i zachowanie zarządzania pamięcią. Używanie pamięci bezpośrednio w bloku ma tendencję do niepotrzebnego tworzenia stron, a także tworzy brudne strony, które komplikują udostępnianie i kopiowanie przy zapisie.
Tak więc bardziej zaawansowaną techniką jest prowadzenie osobnego katalogu. Opracowano także podejścia egzotyczne, w których obszary pamięci wykorzystują tę samą moc dwóch rozmiarów.
Ogólnie rzecz biorąc, odpowiedź brzmi: oddzielna struktura danych jest przypisana do zachowania stanu.
źródło
Aby odpowiedzieć na drugą połowę pytania: tak, możesz, a dość powszechny wzór w C jest następujący:
źródło
Kiedy nazywamy malloc, po prostu zużywa więcej bajtów od jego wymagań. To bardziej bajtowe zużycie zawiera informacje, takie jak suma kontrolna, rozmiar i inne dodatkowe informacje. Kiedy dzwonimy za darmo w tym czasie, bezpośrednio przechodzimy do tych dodatkowych informacji, w których znajduje się adres, a także znajduje się informacja o tym, ile bloków będzie wolne.
źródło
aby odpowiedzieć na drugie pytanie, tak, możesz (w pewnym sensie) skorzystać z tej samej techniki, jak
malloc()
po prostu przypisując pierwszą komórkę w każdej tablicy do rozmiaru tablicy. która pozwala wysłać tablicę bez wysyłania argumentu o dodatkowym rozmiarze.źródło