OK, niektóre odpowiedzi na temat malloc zostały już opublikowane.
Bardziej interesującą częścią jest to, w jaki sposób działa swobodnie (iw tym kierunku można również lepiej zrozumieć malloc).
W wielu implementacjach malloc / free, free zwykle nie zwraca pamięci do systemu operacyjnego (a przynajmniej tylko w rzadkich przypadkach). Powodem jest to, że dostaniesz luki w stosie, a zatem może się zdarzyć, że po prostu uzupełnisz swoją 2 lub 4 GB pamięci wirtualnej lukami. Należy tego unikać, ponieważ gdy tylko pamięć wirtualna zostanie ukończona, będziesz mieć naprawdę duże problemy. Innym powodem jest to, że system operacyjny może obsługiwać tylko te fragmenty pamięci, które mają określony rozmiar i wyrównanie. Mówiąc konkretnie: normalnie system operacyjny obsługuje tylko bloki, które może obsłużyć menedżer pamięci wirtualnej (najczęściej wielokrotności 512 bajtów, np. 4KB).
Zwrócenie 40 bajtów do systemu operacyjnego po prostu nie zadziała. Co robi free?
Wolny umieści blok pamięci na swojej własnej liście wolnych bloków. Zwykle próbuje także połączyć ze sobą sąsiednie bloki w przestrzeni adresowej. Lista wolnych bloków jest po prostu okrągłą listą fragmentów pamięci, które na początku zawierają pewne dane administracyjne. Jest to również powód, dla którego zarządzanie bardzo małymi elementami pamięci za pomocą standardowego malloc / free nie jest wydajne. Każda porcja pamięci wymaga dodatkowych danych, a przy mniejszych rozmiarach dochodzi do większej fragmentacji.
Bezpłatna lista jest także pierwszym miejscem, na które Malloc patrzy, gdy potrzebna jest nowa porcja pamięci. Jest skanowany przed wywołaniem nowej pamięci z systemu operacyjnego. Po znalezieniu fragmentu większego niż potrzebna pamięć jest on dzielony na dwie części. Jeden jest zwracany do osoby dzwoniącej, drugi z powrotem do wolnej listy.
Istnieje wiele różnych optymalizacji tego standardowego zachowania (na przykład w przypadku małych fragmentów pamięci). Ale ponieważ malloc i free muszą być tak uniwersalne, standardowe zachowanie jest zawsze rezerwą, gdy alternatywy nie są użyteczne. Istnieją również optymalizacje w obsłudze wolnej listy - na przykład przechowywanie porcji na listach posortowanych według rozmiarów. Ale wszystkie optymalizacje mają również swoje własne ograniczenia.
Dlaczego kod ulega awarii:
Powodem jest to, że pisząc 9 znaków (nie zapomnij końcowego bajtu zerowego) w obszarze o wielkości 4 znaków, prawdopodobnie nadpiszesz dane administracyjne przechowywane dla innej części pamięci znajdującej się „za” częścią danych ( ponieważ te dane są najczęściej przechowywane „przed” fragmentami pamięci). Gdy wolny, a następnie próbuje umieścić swoją część na bezpłatnej liście, może dotknąć tych danych administracyjnych, a zatem potknąć się o nadpisany wskaźnik. Spowoduje to awarię systemu.
To raczej pełne wdzięku zachowanie. Widziałem również sytuacje, w których niekontrolowany wskaźnik gdzieś nadpisał dane na liście wolnej pamięci, a system nie od razu się zawiesił, ale niektóre podprogramy później. Nawet w systemie o średniej złożoności takie problemy mogą być bardzo trudne do debugowania! W jednym przypadku, w który byłem zaangażowany, zajęło nam (większa grupa programistów) kilka dni, aby znaleźć przyczynę awarii - ponieważ była ona w zupełnie innym miejscu niż wskazane przez zrzut pamięci. To jest jak bomba zegarowa. Wiesz, twój następny „bezpłatny” lub „malloc” się zawiesi, ale nie wiesz dlaczego!
Są to jedne z najgorszych problemów C / C ++ i jeden z powodów, dla których wskaźniki mogą być tak problematyczne.