Jak działają free i malloc w C?

Próbuję rozgryźć, co by się stało, gdybym próbował uwolnić wskaźnik " od środka" na przykład spójrz na następujący kod:

char *ptr = (char*)malloc(10*sizeof(char));

for (char i=0 ; i<10 ; ++i)
{
    ptr[i] = i+10;
}
++ptr;
++ptr;
++ptr;
++ptr;
free(ptr);

Dostaję awarię z nieobsługiwanym błędem wyjątku msg. Chcę zrozumieć, dlaczego i jak działa free tak, że wiem nie tylko, jak go używać, ale także być w stanie zrozumieć dziwne błędy i wyjątki i lepiej debugować mój kodץ

Wielkie dzięki

Author: Vijay Mathew, 2009-12-24

8 answers

Kiedy malloc blok, to faktycznie przydziela trochę więcej pamięci niż prosiłeś. Ta dodatkowa pamięć jest używana do przechowywania informacji, takich jak rozmiar przydzielonego bloku i łącze do następnego wolnego / używanego bloku w łańcuchu bloków, a czasami niektóre "dane ochronne", które pomagają systemowi wykryć, czy zapisujesz po końcu przydzielonego bloku. Ponadto większość alokatorów zaokrągla Całkowity rozmiar i / lub początek części pamięci do wielokrotności bajtów (np. w systemie 64-bitowym może to wyrównać dane do wielokrotności 64 bitów (8 bajtów), ponieważ dostęp do danych z niezrównanych adresów może być trudniejszy i nieefektywny dla procesora/magistrali), więc możesz również skończyć z pewnym "wypełnieniem" (nieużywanymi bajtami).

Kiedy zwalniasz wskaźnik, używa on tego adresu, aby znaleźć specjalne informacje dodane na początku (zwykle) przydzielonego bloku. Jeśli podasz inny adres, uzyska dostęp do pamięci, która zawiera śmieci, a zatem jej zachowanie jest niezdefiniowane (ale najczęściej spowoduje awarię)

Później, jeśli uwolnisz() blok, ale nie "zapomnisz" swojego wskaźnika, możesz przypadkowo spróbować uzyskać dostęp do danych przez ten wskaźnik w przyszłości, a zachowanie będzie niezdefiniowane. Może wystąpić każda z następujących sytuacji:

  • pamięć może być umieszczona na liście wolnych bloków, więc kiedy uzyskasz do niej dostęp, nadal zawiera dane, które tam zostawiłeś, a Twój kod działa normalnie.
  • alokator pamięci mógł dać (część) pamięć do innej części twojego programu, która prawdopodobnie nadpisała (część) twoje stare dane, więc kiedy je przeczytasz, otrzymasz śmieci, które mogą spowodować nieoczekiwane zachowanie lub awarie Twojego kodu. Albo będziesz zapisywać inne dane, powodując, że druga część twojego programu będzie zachowywać się dziwnie w pewnym momencie w przyszłości.
  • pamięć mogła zostać zwrócona do systemu operacyjnego ("strona" pamięci, której już nie używasz, może zostać usunięta z twojego przestrzeń adresowa, więc nie ma już dostępnej pamięci pod tym adresem - zasadniczo nieużywana "dziura" w pamięci aplikacji). Gdy aplikacja próbuje uzyskać dostęp do danych, wystąpi błąd pamięci twardej i zakończy proces.

Dlatego ważne jest, aby upewnić się, że nie używasz wskaźnika po zwolnieniu pamięci, na którą wskazuje - najlepszą praktyką jest ustawienie wskaźnika na NULL po zwolnieniu pamięci, ponieważ możesz łatwo przetestować wartość NULL i spróbować dostęp do pamięci za pomocą wskaźnika NULL spowoduje złe, ale spójne zachowanie, które jest znacznie łatwiejsze do debugowania.

 96
Author: Jason Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-10 19:21:26

Prawdopodobnie wiesz, że powinieneś przekazać dokładnie ten wskaźnik, który otrzymałeś.

Ponieważ free() na początku nie wie, jak duży jest Twój blok, potrzebuje dodatkowych informacji, aby zidentyfikować oryginalny blok z jego adresu, a następnie zwrócić go do wolnej listy. Będzie również próbował połączyć małe wolne bloki z sąsiadami, aby stworzyć bardziej wartościowy duży wolny blok.

Ostatecznie alokator musi mieć metadane dotyczące Twojego bloku, co najmniej będzie trzeba mieć gdzieś zapisaną długość.

Opiszę trzy sposoby, aby to zrobić.

  • Jednym z oczywistych miejsc byłoby przechowywanie go tuż przed zwróconym wskaźnikiem. Może przydzielić blok o kilka bajtów większy niż żądany, zapisać rozmiar w pierwszym słowie, a następnie zwrócić wskaźnik do drugiego słowa.

  • Innym sposobem byłoby posiadanie osobnej mapy opisującej co najmniej długość przydzielonych bloków, używając adresu jako klucz.

  • Implementacja może pobierać pewne informacje z adresu, a niektóre z mapy. Alokator jądra 4.3 BSD (zwany, jak sądzę, "mckusick-Karel allocator") tworzy alokacje power-of-two dla obiektów o rozmiarze mniejszym niż strona i zachowuje tylko rozmiar na stronę, dzięki czemu wszystkie alokacje z danej strony mają jeden rozmiar.

Byłoby to możliwe z niektórymi typami drugiego i prawdopodobnie dowolnego typu trzeciego typu alokatora do detect that you have advanced the pointer and DTRT , though I doubt if any implementation would burn the runtime to do that.

 23
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-24 17:24:09

Większość (jeśli nie wszystkie) implementacji będzie sprawdzać ilość danych, aby zwolnić kilka bajtów przed faktycznym wskaźnikiem, którym manipulujesz. Robienie dzikiego free doprowadzi do uszkodzenia mapy pamięci.

Jeśli twój przykład, kiedy przydzielasz 10 bajtów pamięci, system faktycznie rezerwuje, powiedzmy, 14. Pierwsze 4 zawiera żądaną ilość danych (10), a następnie wartość zwracana malloc jest wskaźnikiem do pierwszego bajtu nieużywanych danych w przydzielonym 14.

Kiedy zadzwonisz free Na tym wskaźniku, system wyszukuje 4 bajty Wstecz, aby wiedzieć, że pierwotnie przydzielono 14 bajtów, aby wiedzieć, ile można zwolnić. Ten system uniemożliwia podanie ilości danych do uwolnienia jako dodatkowego parametru do samego free.

Oczywiście inne implementacje malloc/free może wybrać inny sposób, aby to osiągnąć. Ale generalnie nie obsługują free Na innym wskaźniku niż ten, który został zwrócony przez malloc lub równoważną funkcję.

 10
Author: Zeograd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-24 06:59:29

Z http://opengroup.org/onlinepubs/007908775/xsh/free.html

Funkcja free () powoduje dealokację miejsca wskazywanego przez ptr, czyli udostępnienie go do dalszej alokacji. Jeśli ptr jest wskaźnikiem null, nie występuje żadna akcja. W przeciwnym razie, jeśli argument nie pasuje do wskaźnika wcześniej zwracanego przez funkcję calloc(), malloc(), realloc() lub valloc() lub jeśli przestrzeń jest dealokowana przez wywołanie free() lub realloc (), zachowanie jest niezdefiniowane. Dowolne użycie wskaźnik odnoszący się do wolnej przestrzeni powoduje nieokreślone zachowanie.

 8
Author: PetrosB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-24 06:59:58

To nieokreślone zachowanie - nie rób tego. Tylko free() wskaźniki uzyskane z malloc(), nigdy wcześniej ich nie korygować.

Problem polega na tym, że free() musi być bardzo szybki, więc nie próbuje znaleźć przydziału, do którego należy twój skorygowany adres, ale zamiast tego próbuje zwrócić blok dokładnie pod skorygowanym adresem sterty. Prowadzi to do nieokreślonego zachowania - Zwykle uszkodzenia lub awarii programu.

 7
Author: sharptooth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-24 06:52:33

Uwalniasz zły adres. Zmieniając wartość ptr, zmieniasz adres. free nie ma sposobu, aby wiedzieć, że powinien próbować uwolnić blok zaczynający się od 4 bajtów. Zachowaj oryginalny wskaźnik nienaruszony i uwolnij go zamiast manipulowanego. Jak zauważyli inni, wyniki robienia tego, co robisz, są "niezdefiniowane"... stąd ten nieobsługiwany wyjątek.

 5
Author: Jason D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-24 07:24:47

Nigdy tego nie rób.

Uwalniasz zły adres. Zmieniając wartość ptr, zmieniasz adres. free nie ma sposobu, aby wiedzieć, że powinien próbować uwolnić blok zaczynający się od 4 bajtów. Zachowaj oryginalny wskaźnik nienaruszony i uwolnij go zamiast manipulowanego. Jak zauważyli inni, wyniki robienia tego, co robisz, są "niezdefiniowane"... stąd nieobsługiwany wyjątek

 2
Author: Jeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 06:23:28

Zaczerpnięte z książki: rozumienie i używanie wskaźników C

Gdy pamięć jest przydzielana, dodatkowe informacje są przechowywane jako część struktury danych utrzymywanej przez menedżera sterty. Informacje te obejmują między innymi rozmiar bloku i są zazwyczaj umieszczane bezpośrednio obok przydzielonego bloku.

 2
Author: Koray Tugay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-09 13:30:15