DZWON

Są tacy, którzy czytają tę wiadomość przed wami.
Zapisz się, aby otrzymywać najnowsze artykuły.
E-mail
Imię
Nazwisko
Jak chcesz przeczytać The Bell
Bez spamu

Dynamiczna i statyczna alokacja pamięci. Zalety i wady. Alokacja pamięci dla pojedynczych zmiennych przy użyciu operatorów new i delete. Możliwe sytuacje krytyczne podczas przydzielania pamięci. Inicjalizacja przy alokacji pamięci

1. Dynamiczna i statyczna (stała) alokacja pamięci. Główne różnice

Aby pracować z tablicami informacji, programy muszą przydzielić pamięć dla tych tablic. Do alokacji pamięci dla tablic zmiennych wykorzystuje się odpowiednie operatory, funkcje itp. W języku programowania C ++ wyróżnia się następujące metody alokacji pamięci:

1. Statyczny (naprawiony) alokacja pamięci. W takim przypadku pamięć jest przydzielana tylko raz w czasie kompilacji. Wielkość przydzielonej pamięci jest stała i niezmieniona do końca wykonywania programu. Przykładem takiej alokacji jest deklaracja tablicy 10 liczb całkowitych:

int M; // pamięć tablicy jest przydzielana raz, rozmiar pamięci jest stały

2. Dynamiczny alokacja pamięci. W takim przypadku używana jest kombinacja operatorów new i delete. Operator new przydziela pamięć dla zmiennej (tablicy) w specjalnym obszarze pamięci zwanym „stertą”. Operator delete zwalnia przydzieloną pamięć. Każdy nowy operator musi mieć własny operator usuwania.

2. Zalety i wady stosowania dynamicznych i statycznych metod alokacji pamięci

Dynamiczna alokacja pamięci ma następujące zalety w stosunku do statycznej alokacji pamięci:

  • pamięć jest przydzielana programowo w razie potrzeby;
  • bez niepotrzebnego marnowania niewykorzystanej pamięci. Alokuje się tyle pamięci, ile potrzeba i jeśli potrzeba;
  • możliwe jest przydzielenie pamięci na tablice informacji, których rozmiar jest a priori nieznany. Określenie rozmiaru tablicy następuje podczas wykonywania programu
  • wygodnie jest ponownie przydzielić pamięć. Innymi słowy, wygodnie jest przydzielić nowy fragment dla tej samej tablicy, jeśli trzeba przydzielić dodatkową pamięć lub zwolnić niepotrzebną pamięć;
  • przy statycznym sposobie przydzielania pamięci trudno jest ponownie przydzielić pamięć dla zmiennej tablicowej, ponieważ jest ona już przydzielona na stałe. W przypadku metody wyboru dynamicznego odbywa się to w prosty i wygodny sposób.

Zalety statycznego sposobu przydzielania pamięci:

  • lepiej jest stosować statyczną (stałą) alokację pamięci, gdy rozmiar tablicy informacyjnej jest znany i pozostaje niezmieniony przez cały czas wykonywania całego programu;
  • statyczna alokacja pamięci nie wymaga dodatkowych operacji zwalniania alokacji przy użyciu operatora delete. Skutkuje to mniejszą liczbą błędów programowania. Każdy nowy operator musi mieć swój własny operator usuwania;
  • naturalność (naturalność) prezentacji kodu programu działającego na tablicach statycznych.

W zależności od wykonywanego zadania programista musi być w stanie poprawnie określić, która metoda alokacji pamięci jest odpowiednia dla jednej lub innej zmiennej (tablicy).

3. Jak przydzielić pamięć za pomocą nowego operatora dla pojedynczej zmiennej? Forma ogólna.

Ogólna forma przydzielania pamięci dla pojedynczej zmiennej za pomocą operatora new jest następująca:

ptrName \u003d nowy typ;
  • ptrName - nazwa zmiennej (wskaźnik), która będzie wskazywać na przydzieloną pamięć;
  • rodzaj - typ zmiennej. Wielkość pamięci jest wystarczająca do przechowywania w niej wartości zmiennej tego typu rodzaj .
4. Jak zwolnić pamięć przydzieloną pojedynczej zmiennej za pomocą operatora usuwania? Forma ogólna

Jeżeli pamięć na zmienną jest przydzielona przez nowy operator, to po zakończeniu używania zmiennej pamięć tę należy zwolnić operatorem kasowania. W C ++ jest to warunek wstępny. Jeśli pamięć nie zostanie zwolniona, pozostanie ona przydzielona (zajęta), ale żaden program nie może jej używać. W takim przypadku będzie "wyciek pamięci" (wyciek pamięci).

W językach programowania Java, C # nie ma potrzeby zwalniania pamięci po przydzieleniu. Robi to zbieracz śmieci.

Ogólna postać operatora usuwania dla pojedynczej zmiennej to:

usuń ptrName;

gdzie ptrName - nazwa wskaźnika, dla którego pamięć została wcześniej przydzielona przez nowego operatora. Po wykonaniu operatora usuwania wskaźnik ptrName wskazuje na dowolny fragment pamięci, który nie jest zarezerwowany (przydzielony).

5. Przykłady alokowania (nowego) i zwalniania (usuwania) pamięci dla wskaźników typów bazowych

Przykłady demonstrują użycie operatorów new i delete. Przykłady są uproszczone.

Przykład 1. Wskaźnik do typu int. Najprostszy przykład

// alokacja pamięci za pomocą operatora new int * p; // wskaźnik do int p \u003d nowy int; // alokacja pamięci dla wskaźnika * p \u003d 25; // zapis wartości do pamięci // użyj pamięci przydzielonej wskaźnikowi int d; d \u003d * p; // d \u003d 25 // wolna pamięć przydzielona wskaźnikowi - wymagana usuń p;

Przykład 2. Wskaźnik do wpisania double

// alokacja pamięci dla wskaźnika do podwojenia double * pd \u003d NULL; pd \u003d nowe podwójne; // alokacja pamięci if (pd! \u003d NULL) (* pd \u003d 10,89; // zapis wartości podwójne d \u003d * pd; // d \u003d 10,89 - użyj w programie // wolna pamięć usuń pd; )
6. Co to jest wyciek pamięci?

« Wyciek pamięci"- ma to miejsce, gdy pamięć na zmienną jest przydzielana przez nowego operatora, a na końcu programu nie jest zwalniana przez operatora kasującego." W takim przypadku pamięć w systemie pozostaje zajęta, chociaż nie ma już potrzeby jej używania, ponieważ program, który z niej korzystał, już dawno zakończył swoją pracę.

Wyciek pamięci to typowy błąd programisty. Jeśli „wyciek pamięci” powtórzy się wielokrotnie, to możliwa sytuacja, w której cała dostępna pamięć w komputerze zostanie „zajęta”. Doprowadzi to do nieprzewidywalnych konsekwencji dla systemu operacyjnego.

7. Jak alokować pamięć za pomocą nowego operatora z przechwytywaniem sytuacji krytycznej, w której pamięć może nie zostać przydzielona? Wyjątek bad_alloc. Przykład

Podczas korzystania z nowego operatora możliwe jest, że pamięć nie zostanie przydzielona. Pamięć może nie zostać przydzielona w następujących sytuacjach:

  • jeśli nie ma wolnej pamięci;
  • wielkość wolnej pamięci jest mniejsza niż określona w nowym operatorze.

W tym przypadku zgłaszany jest wyjątek bad_alloc. Program może wychwycić tę sytuację i odpowiednio sobie z nią poradzić.

Przykład. Przykład uwzględnia sytuację, w której pamięć może nie zostać przydzielona przez nowego operatora. W takim przypadku podejmowana jest próba przydzielenia pamięci. Jeśli próba się powiedzie, program będzie kontynuowany. Jeśli próba się nie powiedzie, funkcja kończy działanie z kodem -1.

int main () ( // zadeklaruj tablicę wskaźników do float float * ptrArray; próbować ( // spróbuj przydzielić pamięć na 10 wartości zmiennoprzecinkowych ptrArray \u003d new float; ) catch (bad_alloc ba) (cout<< << endl; cout << ba.what() << endl; return -1; // wyjdź z funkcji } // jeśli wszystko jest w porządku, użyj tablicy dla (int i \u003d 0; i< 10; i++) ptrArray[i] = i * i + 3; int d = ptrArray; cout << d << endl; delete ptrArray; // zwolnij pamięć przydzieloną dla tablicy return 0; )
8. Alokacja pamięci dla zmiennej z jednoczesną inicjalizacją. Forma ogólna. Przykład

Operator alokacji new dla pojedynczej zmiennej umożliwia jednoczesną inicjalizację wartością tej zmiennej.

Ogólnie alokacja pamięci dla zmiennej z jednoczesną inicjalizacją ma postać

ptrName\u003d nowy typ ( wartość)
  • ptrName- nazwa zmiennej wskaźnikowej, dla której jest przydzielona pamięć;
  • rodzaj - typ, na który wskazuje wskaźnik ptrName ;
  • wartość - wartość ustawiana dla przydzielonego obszaru pamięci (wartość według wskaźnika).

Przykład. Alokacja pamięci dla zmiennych z jednoczesną inicjalizacją. Poniżej znajduje się funkcja main () dla aplikacji konsolowej. Pokazano alokację pamięci z jednoczesną inicjalizacją. Sytuacja jest również brana pod uwagę, gdy próba alokacji pamięci nie powiedzie się (sytuacja krytyczna bad_alloc).

#include „stdafx.h” #include using namespace std; int main () ( // alokacja pamięci z jednoczesną inicjalizacją float * pF; int * pI; char * pC; próbować ( // spróbuj przydzielić pamięć na zmienne z jednoczesną inicjalizacją pF \u003d nowa liczba zmiennoprzecinkowa (3,88); // * pF \u003d 3,88 pI \u003d new int (250); // * pI \u003d 250 pC \u003d nowy znak ("M"); // * pC \u003d "M") catch (bad_alloc ba) (cout<< Wyjątek: brak przydzielonej pamięci << endl; cout << ba.what() << endl; return -1; // wyjdź z funkcji } // jeśli pamięć jest przydzielona, \u200b\u200bto za pomocą wskaźników pF, pI, pC float f \u003d * pF; // f \u003d 3,88 int i \u003d * pI; // i \u003d 250; char c; c \u003d * pC; // c \u003d „M” // wyświetl zainicjowane wartości cout<< "*pF = " << f<< endl; cout << "*pI = " << i << endl; cout << "*pC = " << c << endl; // zwalnia pamięć przydzieloną wcześniej dla wskaźników usuń pF; usuń pI; usuń komputer PC; return 0; )

Odkryliśmy możliwości dynamicznej alokacji pamięci. Co to znaczy? Oznacza to, że przy dynamicznej alokacji pamięci pamięć jest rezerwowana nie na etapie kompilacji, ale na etapie wykonywania programu. A to daje nam możliwość wydajniejszego przydzielania pamięci, głównie dla tablic. Dzięki dynamicznej alokacji pamięci nie musimy z góry ustawiać rozmiaru tablicy, zwłaszcza że nie zawsze wiadomo, jaki rozmiar powinna mieć tablica. Następnie przyjrzyjmy się, jak można przydzielić pamięć.

Alokacja pamięci w C (funkcja malloc)

Funkcja malloc () jest zdefiniowana w pliku nagłówkowym stdlib.h i służy do inicjalizacji wskaźników z wymaganą ilością pamięci. Pamięć jest przydzielana z sektora pamięci RAM dostępnej dla dowolnych programów uruchomionych na danej maszynie. Argumentem jest liczba bajtów pamięci do przydzielenia, funkcja zwraca - wskaźnik do przydzielonego bloku w pamięci. Funkcja malloc () działa jak każda inna funkcja, nic nowego.

Ponieważ różne typy danych mają różne wymagania dotyczące pamięci, musimy w jakiś sposób nauczyć się, jak uzyskać rozmiar w bajtach dla różnych typów danych. Na przykład potrzebujemy kawałek pamięci na tablicę wartości int - jest to jeden rozmiar pamięci, a jeśli musimy przydzielić pamięć dla tablicy o tym samym rozmiarze, ale już typu char, jest to inny rozmiar. Dlatego musisz jakoś obliczyć rozmiar pamięci. Można to zrobić za pomocą operacji sizeof (), która pobiera wyrażenie i zwraca jego rozmiar. Na przykład sizeof (int) zwróci liczbę bajtów potrzebną do przechowywania int. Rozważmy przykład:

#zawierać int * ptrVar \u003d malloc (sizeof (int));

W tym przykładzie w wiersz 3 wskaźnik ptrVar ma przypisany adres do lokalizacji pamięci, której rozmiar odpowiada typowi danych int. Automatycznie ten obszar pamięci staje się niedostępny dla innych programów. Oznacza to, że gdy przydzielona pamięć stanie się niepotrzebna, należy ją jawnie zwolnić. Jeśli pamięć nie zostanie jawnie zwolniona, to po zakończeniu programu pamięć nie zostanie zwolniona dla systemu operacyjnego, nazywa się to wyciekiem pamięci. Możesz również określić rozmiar przydzielonej pamięci, która ma zostać przydzielona, \u200b\u200bprzekazując pusty wskaźnik, oto przykład:

Int * ptrVar \u003d malloc (sizeof (* ptrVar));

Co tu się dzieje? Operacja sizeof (* ptrVar) oszacuje rozmiar fragmentu pamięci, do którego odnosi się wskaźnik. Ponieważ ptrVar jest wskaźnikiem do fragmentu pamięci typu int, sizeof () zwróci rozmiar liczby całkowitej. Oznacza to, że zasadniczo na podstawie pierwszej części definicji wskaźnika obliczany jest rozmiar drugiej części. Dlaczego więc tego potrzebujemy? Może to być potrzebne, jeśli nagle zajdzie potrzeba zmiany definicji wskaźnika, na przykład int, na float, a wtedy nie musimy zmieniać typu danych w dwóch częściach definicji wskaźnika. Wystarczy, że zmienimy pierwszą część:

Float * ptrVar \u003d malloc (sizeof (* ptrVar));

Jak widać, notacja ta ma jedną bardzo mocną stronę, nie powinniśmy wywoływać funkcji malloc () przy użyciu sizeof (float). Zamiast tego przekazaliśmy wskaźnik do typu float do malloc (), w którym to przypadku rozmiar przydzielonej pamięci zostanie automatycznie określony przez siebie!

Jest to szczególnie przydatne, jeśli chcesz przydzielić pamięć daleko od definicji wskaźnika:

Float * ptrVar; / *. ... ... sto linii kodu * /. ... ... ptrVar \u003d malloc (sizeof (* ptrVar));

Gdybyś zastosował konstrukcję alokacji pamięci z operacją sizeof (), to musiałbyś znaleźć definicję wskaźnika w kodzie, przyjrzeć się jego typowi danych i dopiero wtedy byłbyś w stanie poprawnie alokować pamięć.

Zwalnianie przydzielonej pamięci

Pamięć jest zwalniana za pomocą funkcji free (). Oto przykład:

Bezpłatne (ptrVar);

Po zwolnieniu pamięci dobrą praktyką jest zresetowanie wskaźnika do zera, czyli przypisanie * ptrVar \u003d 0. Jeśli przypiszesz 0 do wskaźnika, wskaźnik stanie się pusty, innymi słowy, nie będzie nigdzie wskazywał. Zawsze po zwolnieniu pamięci przypisz 0 do wskaźnika, w przeciwnym razie nawet po zwolnieniu pamięci wskaźnik nadal na niego wskazuje, co oznacza, że \u200b\u200bmożesz przypadkowo uszkodzić inne programy, które mogą korzystać z tej pamięci, ale nawet nic o tym nie wiesz dowiesz się i pomyślisz, że program działa poprawnie.

PS: Każdy, kto lubi edycję wideo, może być zainteresowany tym edytorem wideo Windows 7. Edytor wideo nazywa się Movavi, może ktoś już go zna lub nawet z nim pracował. Dzięki temu programowi w języku rosyjskim możesz łatwo dodać wideo z kamery, poprawić jakość i zastosować piękne efekty wideo.

Zanim zagłębimy się w programowanie obiektowe, będziemy musieli zrobić małą dygresję na temat pracy z pamięcią w programie C ++. Nie możemy napisać żadnego złożonego programu bez możliwości przydzielenia pamięci w czasie wykonywania i uzyskania do niej dostępu.
W C ++ obiekty mogą być przydzielane statycznie - w czasie kompilacji lub dynamicznie - w czasie wykonywania, przez wywołanie funkcji z biblioteki standardowej. Główną różnicą w stosowaniu tych metod jest ich skuteczność i elastyczność. Alokacja statyczna jest bardziej wydajna, ponieważ pamięć jest przydzielana przed wykonaniem programu, ale jest znacznie mniej elastyczna, ponieważ musimy z wyprzedzeniem znać typ i rozmiar przydzielanego obiektu. Na przykład wcale nie jest łatwo umieścić zawartość pliku tekstowego w statycznej tablicy ciągów: musimy wcześniej znać jego rozmiar. Zadania wymagające wcześniejszego przechowywania i przetwarzania nieznanej liczby elementów zwykle wymagają dynamicznej alokacji pamięci.
Do tej pory wszystkie nasze przykłady wykorzystywały statyczną alokację pamięci. Powiedzmy definicję zmiennej ival

Int ival \u003d 1024;

zmusza kompilator do przydzielenia obszaru w pamięci dostatecznie dużego, aby przechować zmienną int, powiązania nazwy ival z tym obszarem i umieszczenia tam wartości 1024. Wszystko to jest wykonywane w czasie kompilacji, przed wykonaniem programu.
Z obiektem ival są powiązane dwie wartości: aktualna wartość zmiennej, w tym przypadku 1024, oraz adres obszaru pamięci, w którym ta wartość jest przechowywana. Możemy odnosić się do jednej z tych dwóch wielkości. Kiedy piszemy:

Int ival2 \u003d ival + 1;

następnie odwołujemy się do wartości zawartej w zmiennej ival: dodajemy do niej 1 i inicjalizujemy zmienną ival2 tą nową wartością, 1025. Jak możemy odwołać się do adresu, pod którym znajduje się zmienna?
C ++ ma wbudowany typ wskaźnika, który służy do przechowywania adresów obiektów. Aby zadeklarować wskaźnik zawierający adres zmiennej ival, musimy napisać:

Int * pint; // wskaźnik do obiektu typu int

Istnieje również specjalna operacja pobierania adresu, oznaczona symbolem &. Jego wynikiem jest adres obiektu. Poniższa instrukcja przypisuje adres zmiennej ival do wskaźnika pinta:

Int * pint; pint \u003d // pint pobiera wartość adresu ival

Do obiektu, którego adres zawiera pint (w naszym przypadku ival), możemy odwołać się za pomocą operacji dereferencjanazywane również adresowanie pośrednie... Ta operacja jest oznaczona *. Oto jak dodać jeden pośrednio do ival, używając jego adresu:

* kufel \u003d * kufel + 1; // niejawnie zwiększa wartość ival

To wyrażenie robi dokładnie to samo, co

Ival \u003d ival + 1; // jawnie zwiększa ival

Ten przykład nie ma żadnego sensu: używanie wskaźnika do pośredniego manipulowania zmienną ival jest mniej wydajne i mniej opisowe. Zamieściliśmy ten przykład tylko po to, aby zapewnić bardzo podstawowe zrozumienie wskaźników. W rzeczywistości wskaźniki są najczęściej używane do manipulowania dynamicznie umieszczonymi obiektami.
Główne różnice między statyczną i dynamiczną alokacją pamięci są następujące:

  • obiekty statyczne są oznaczane przez nazwane zmienne, a działania na tych obiektach są wykonywane bezpośrednio przy użyciu ich nazw. Obiekty dynamiczne nie mają własnych nazw, a działania na nich wykonywane są pośrednio za pomocą wskaźników;
  • alokacja i zwalnianie pamięci dla obiektów statycznych jest wykonywane przez kompilator automatycznie. Programista nie musi sam się tym zająć. Za przydzielanie i zwalnianie pamięci dla obiektów dynamicznych całkowicie odpowiada programista. Jest to trudne zadanie i podczas jego rozwiązywania łatwo jest popełnić błędy. Operatory new i delete służą do manipulowania dynamicznie przydzieloną pamięcią.

Nowy operator ma dwie formy. Pierwsza forma przydziela pamięć dla pojedynczego obiektu określonego typu:

Int * pint \u003d new int (1024);

Tutaj operator new przydziela pamięć dla nienazwanego obiektu typu int, inicjuje go na 1024 i zwraca adres utworzonego obiektu. Ten adres jest używany do inicjalizacji wskaźnika kufla. Wszystkie akcje na takim nienazwanym obiekcie są wykonywane przez wyłuskiwanie tego wskaźnika, ponieważ nie jest możliwe jawne manipulowanie obiektem dynamicznym.
Druga postać operatora new alokuje pamięć dla tablicy o danym rozmiarze, składającej się z elementów określonego typu:

Int * pia \u003d new int;

W tym przykładzie pamięć jest alokowana dla tablicy czterech elementów int. Niestety ta postać nowego operatora nie pozwala na inicjalizację elementów tablicy.
Pewne zamieszanie jest spowodowane faktem, że obie formy nowego operatora zwracają ten sam wskaźnik, w naszym przykładzie jest to wskaźnik do liczby całkowitej. Zarówno pint, jak i pia są zadeklarowane dokładnie tak samo, ale pint wskazuje na pojedynczy int, a pia wskazuje na pierwszy element tablicy czterech obiektów int.
Gdy obiekt dynamiczny nie jest już potrzebny, musimy jawnie zwolnić przydzieloną mu pamięć. Odbywa się to za pomocą operatora delete, który podobnie jak new ma dwie formy - dla pojedynczego obiektu i dla tablicy:

// zwolnij pojedynczy obiekt delete pint; // zwolnij tablicę delete pia;

Co się stanie, jeśli zapomnimy o zwolnieniu przydzielonej pamięci? Pamięć zostanie zmarnowana, będzie nieużywana, ale nie można jej zwrócić do systemu, ponieważ nie mamy do niej wskaźnika. Zjawisko to otrzymało specjalną nazwę wyciek pamięci... W końcu program ulegnie awarii z powodu niewystarczającej ilości pamięci (zakładając oczywiście, że będzie działał wystarczająco długo). Niewielki wyciek jest trudny do wykrycia, ale istnieją narzędzia, które pomogą Ci to zrobić.
Nasz zwięzły przegląd dynamicznej alokacji pamięci i użycia wskaźnika prawdopodobnie wywołał więcej pytań niż odpowiedzi. W sekcji 8.4 omówiono szczegółowo poruszone kwestie. Nie moglibyśmy jednak obejść się bez tej dygresji, ponieważ klasa Array, którą zaprojektujemy w kolejnych podrozdziałach, opiera się na wykorzystaniu dynamicznie przydzielanej pamięci.

Ćwiczenie 2.3

Wyjaśnij różnicę między czterema obiektami:

a) int ival \u003d 1024; (b) int * pi \u003d (c) int * pi2 \u003d new int (1024); (d) int * pi3 \u003d new int;

Ćwiczenie 2.4

Co robi następujący fragment kodu? Co to jest błąd logiczny? (Zwróć uwagę, że operacja pobierania index () jest poprawnie stosowana do wskaźnika pia. Wyjaśnienie tego faktu można znaleźć w sekcji 3.9.2.)

Int * pi \u003d new int (10); int * pia \u003d new int;
while (* pi< 10) {
pia [* pi] \u003d * pi; * pi \u003d * pi + 1;
) usuń pi; usuń pia;

Więc. trzeci typ, dla nas najbardziej interesujący w tym temacie, to typ pamięci dynamicznej.

Jak wcześniej pracowaliśmy z tablicami? int a Jak teraz pracujemy? Wybieramy tyle, ile potrzeba:

#zawierać < stdio.h> #zawierać < stdlib.h> int main () (rozmiar_t rozmiar; // Utwórz wskaźnik do int // jest zasadniczo pustą tablicą. int * list; scanf ( „% lu”, & rozmiar); // Przydziel pamięć dla elementów o rozmiarze int // a nasza „pusta tablica” odnosi się teraz do tej pamięci. list \u003d (int *) malloc (size * sizeof (int)); dla (int i \u003d 0; i< size; ++i) { scanf („% d” < size; ++i) { printf („% d”, * (lista + i)); ) // Nie zapomnij posprzątać po sobie! wolny (lista); ) // *

Void * malloc (rozmiar_t rozmiar);

Ale ogólnie jest to funkcja, która przydziela rozmiar bajtów niezainicjowanej pamięci (nie zera, ale śmieci).

Jeśli alokacja się powiedzie, zwracany jest wskaźnik do pierwszego bajtu przydzielonej pamięci.

Jeśli się nie powiedzie - NULL. Ponadto errno będzie równe ENOMEM (przyjrzymy się tej wspaniałej zmiennej później). Oznacza to, że bardziej poprawne było napisanie:

#zawierać < stdio.h> #zawierać < stdlib.h> int main () (rozmiar_t rozmiar; int * lista; scanf ( „% lu”, & rozmiar); list \u003d (int *) malloc (size * sizeof (int)); if (list \u003d\u003d NULL) (goto error;) for (int i \u003d 0; i< size; ++i) { scanf („% d”, list + i); ) dla (int i \u003d 0; i< size; ++i) { printf („% d”, * (lista + i)); ) wolny (lista); return 0; błąd: powrót 1; ) // *

Nie musisz czyścić wskaźnika NULL

#zawierać < stdlib.h> int main () (wolny (NULL);)

- w tym samym brzęku wszystko pójdzie dobrze (nic nie zrobi), ale w bardziej egzotycznych przypadkach może to spowodować awarię programu.

Obok malloc i wolnej many, możesz również zobaczyć:

    void * calloc (licznik size_t, rozmiar size_t);

    Jak również malloc przydzieli pamięć dla zliczania obiektów o rozmiarze bajtów. Przydzielona pamięć jest inicjowana zerami.

    void * realloc (void * ptr, size_t size);

    Ponownie przydziela (jeśli to możliwe) pamięć wskazywaną przez ptr w rozmiarze bajtów. Jeśli nie ma wystarczającej ilości miejsca, aby zwiększyć przydzieloną pamięć wskazywaną przez ptr, realloc tworzy nową alokację (alokację), kopiuje stare dane wskazywane przez ptr, zwalnia starą alokację i zwraca wskaźnik do przydzielonej pamięci.

    Jeśli ptr ma wartość NULL, realloc działa tak samo, jak wywołanie malloc.

    Jeśli size wynosi zero, a ptr nie jest równe NULL, alokowany jest fragment pamięci o minimalnym rozmiarze, a oryginalny jest zwalniany.

    void * reallocf (void * ptr, size_t size);

    Pojęcie z API FreeBSD. Podobnie jak realloc, ale jeśli nie uda się ponownej alokacji, czyści odebrany wskaźnik.

    void * valloc (rozmiar_t rozmiar);

    Podobnie jak malloc, ale przydzielona pamięć jest wyrównana do strony.

Program może przechowywać informacje w pamięci głównej komputera na dwa główne sposoby. Pierwsza wykorzystuje zmienne globalne i lokalne, w tym tablice, struktury i klasy. W przypadku globalnych i statycznych zmiennych lokalnych miejsce przechowywania jest stałe na cały czas wykonywania programu. W przypadku zmiennych lokalnych pamięć jest alokowana na stosie. Chociaż Borland C ++ implementuje te zmienne bardzo wydajnie, ich użycie wymaga od programisty znajomości z wyprzedzeniem ilości pamięci, która będzie wymagana podczas wykonywania programu.

Drugim sposobem przechowywania informacji jest użycie systemu dynamicznej alokacji pamięci Borland C ++. W tej metodzie pamięć do przechowywania informacji jest przydzielana z wolnego obszaru pamięci w razie potrzeby i zwracana z powrotem, tj. jest uwalniany, gdy zniknie potrzeba. Obszar wolnej pamięci leży między obszarem pamięci, w którym znajduje się program, a stosem. Ten obszar nazywa się stertą i jest używany do żądań sterty.

Zaletą korzystania z pamięci dynamicznej jest to, że ta sama pamięć może być używana do przechowywania różnych informacji podczas wykonywania programu. Ponieważ pamięć jest przydzielana do określonego celu i zwalniana po zakończeniu jej używania, możesz użyć tej samej pamięci w innym momencie do różnych celów w innej części programu. Kolejną zaletą dynamicznej alokacji pamięci jest możliwość wykorzystania jej do tworzenia połączonych list, drzew binarnych i innych dynamicznych struktur danych.

Rdzeniem dynamicznej alokacji pamięci w C jest malloc () i free (), które są częścią standardowej biblioteki. Za każdym razem, gdy malloc () wysyła żądanie alokacji pamięci, alokowany jest fragment dostępnej wolnej pamięci. Ilekroć ta pamięć jest zwalniana za pomocą funkcji free (), jest ona zwracana z powrotem do systemu.

Język C ++ definiuje dwa dynamiczne operatory alokacji pamięci, new i delete.

Standard ANSI C definiuje tylko cztery funkcje dynamicznej alokacji pamięci: calloc (), malloc (), free () i realloc (). Jednak Borland C ++ zawiera kilka innych funkcji dynamicznej alokacji pamięci. Podczas kompilowania kodu dla nowoczesnego modelu 32-bitowej pamięci pamięć jest płaska i zwykle używane są tylko cztery standardowe funkcje alokacji pamięci.

Standard ANSI C określa, że \u200b\u200binformacje nagłówka wymagane do dynamicznej alokacji pamięci są zawarte w pliku stdlib.h. Borland C ++ pozwala jednak na używanie nagłówków stdlib.h lub przydziel.h. Używamy tutaj pliku nagłówkowego stdlib.h, ponieważ zapewnia on przenośność. Niektóre inne funkcje dynamicznej alokacji pamięci wymagają plików nagłówkowych przydziel.h, malloc.h lub dos.h. Musisz zwrócić szczególną uwagę na to, który plik nagłówkowy jest potrzebny do użycia każdej funkcji.

DZWON

Są tacy, którzy czytają tę wiadomość przed wami.
Zapisz się, aby otrzymywać najnowsze artykuły.
E-mail
Imię
Nazwisko
Jak chcesz przeczytać The Bell
Bez spamu