Stack, Static i Heap w C++

Szukałem, ale nie zrozumiałem zbyt dobrze tych trzech pojęć. Kiedy muszę korzystać z alokacji dynamicznej (w stercie) i jaka jest jej realna zaleta? Jakie są problemy statyczne i stosowe? Czy mogę napisać całą aplikację bez przydzielania zmiennych w stercie?

Słyszałem, że inne języki zawierają "garbage collector", więc nie musisz martwić się o pamięć. Co robi Śmieciarz?

Co można zrobić manipulując pamięcią sam nie mogłeś tego zrobić używając tego śmieciarza?

Kiedyś ktoś mi powiedział, że tą deklaracją:

int * asafe=new int;

Mam "wskaźnik do wskaźnika". Co to znaczy? Różni się od:

asafe=new int;

?

Author: user2864740, 2009-01-03

9 answers

Podobne pytanie zostało zadane, ale nie pytało o statykę.

Podsumowanie czym jest pamięć statyczna, sterta i stos:

  • Zmienna statyczna jest w zasadzie zmienną globalną, nawet jeśli nie możesz uzyskać do niej dostępu globalnie. Zwykle jest dla niego adres, który znajduje się w samym pliku wykonywalnym. Jest tylko jedna kopia dla całego programu. Bez względu na to, ile razy wchodzi się do wywołania funkcji (lub klasy) (i w ilu wątkach!) zmienna odnosi się do tego samego Miejsce Pamięci.

  • Sterta jest zbiorem pamięci, które mogą być używane dynamicznie. Jeśli chcesz mieć 4kb dla obiektu, dynamiczny alokator przejrzy listę wolnego miejsca na stercie, wybierze fragment 4kb i da ci go. Ogólnie rzecz biorąc, dynamiczny alokator pamięci (malloc, new, itd.) rozpoczyna się na końcu pamięci i działa wstecz.

  • Wyjaśnienie, jak stos rośnie i kurczy się, jest nieco poza zakresem tej odpowiedzi, ale wystarczy powiedzieć, że zawsze Dodaj i usuń tylko z końca. Stosy zwykle zaczynają się wysoko i rosną w dół do niższych adresów. Kończy się pamięć, gdy stos spotyka się z alokatorem dynamicznym gdzieś pośrodku (ale odnoszą się do pamięci fizycznej i wirtualnej). Wiele wątków wymaga wielu stosów (proces zwykle rezerwuje minimalny rozmiar stosu).

Kiedy chcesz użyć każdego z nich:

  • Statyka / globale są przydatne dla pamięci, które znasz zawsze będzie trzeba i wiesz, że nigdy nie chcesz deallocate. (Nawiasem mówiąc, środowiska wbudowane mogą być uważane za posiadające tylko pamięć statyczną... stos i sterta są częścią znanej przestrzeni adresowej współdzielonej przez trzeci typ pamięci: kod programu. Programy często wykonują dynamiczną alokację z pamięci statycznej, gdy potrzebują takich rzeczy, jak listy połączone. Ale niezależnie od tego, sama pamięć statyczna (bufor) nie jest sama "przydzielana" , ale raczej inne obiekty są przydzielane z pamięć przechowywana w tym celu przez bufor. Można to zrobić również w grach nie wbudowanych, a gry konsolowe często unikają wbudowanych mechanizmów pamięci dynamicznej na rzecz ścisłego kontrolowania procesu alokacji za pomocą buforów o wstępnie ustawionych rozmiarach dla wszystkich alokacji.)

  • Zmienne stosu są przydatne, gdy wiesz, że dopóki funkcja znajduje się w obszarze (gdzieś na stosie), będziesz chciał, aby zmienne pozostały. Stosy są ładne dla zmiennych, które są potrzebne do kod, w którym się znajdują, ale który nie jest potrzebny poza tym kodem. Są one również bardzo miłe, gdy uzyskujesz dostęp do zasobu, takiego jak plik, i chcesz, aby zasób automatycznie zniknął, gdy zostawisz ten kod.

  • Alokacje sterty (dynamicznie przydzielana pamięć) są przydatne, gdy chcesz być bardziej elastyczny niż powyższe. Często wywoływana jest funkcja odpowiadająca na zdarzenie (użytkownik kliknie przycisk "Utwórz pudełko"). Właściwa odpowiedź może wymagać przydzielenia nowy obiekt (nowy obiekt Box), który powinien pozostać długo po zakończeniu funkcji, więc nie może znajdować się na stosie. Ale nie wiesz, ile skrzynek chcesz na początku programu, więc to nie może być statyczne.

Wywóz Śmieci

Słyszałem ostatnio dużo o tym, jak świetni są śmieciarze, więc może przydałby się trochę innego głosu.

Wywóz śmieci jest wspaniałym mechanizmem, gdy wydajność nie jest wielkim problemem. Słyszę GCs są coraz lepsze i bardziej wyrafinowane, ale faktem jest, że możesz być zmuszony do zaakceptowania kary za wydajność (w zależności od przypadku użycia). A jeśli jesteś leniwy, to nadal może nie działać prawidłowo. W najlepszych momentach, śmieciarze zdają sobie sprawę, że pamięć znika, gdy zdają sobie sprawę, że nie ma więcej odniesień do niej (zobacz liczenie odniesień). Ale jeśli masz obiekt, który odnosi się do siebie (ewentualnie przez odniesienie do innego obiektu, który odwołuje się z powrotem), to zliczanie odniesień sam nie wskaże, że pamięć może zostać usunięta. W tym przypadku GC musi spojrzeć na całą zupę referencyjną i dowiedzieć się, czy istnieją jakieś wyspy, o których mowa tylko same. Od ręki, domyślam się, że to operacja O (n^2), ale cokolwiek to jest, może być źle, jeśli w ogóle chodzi o wydajność. (Edit: Martin B wskazuje, Że Jest O(n) dla racjonalnie wydajnych algorytmów. To nadal O (n) za dużo, jeśli chodzi o wydajność i można deallocate w stałym czasie bez zbierania śmieci.)

Osobiście, kiedy słyszę, że ludzie mówią, że C++ nie ma śmieciarki, mój umysł oznacza to jako cechę C++, ale prawdopodobnie jestem w mniejszości. Prawdopodobnie najtrudniejszą rzeczą dla ludzi, aby dowiedzieć się o programowaniu w C i C++ są wskaźniki i jak poprawnie obsługiwać ich dynamiczne alokacje pamięci. Niektóre inne języki, takie jak Python, byłyby straszne bez GC, więc myślę, że sprowadza się to do tego, co chcesz z języka. Jeśli chcesz niezawodnej wydajności, to C++ bez usuwania śmieci jest jedyną rzeczą po tej stronie Fortrana, o której mogę myśleć. Jeśli chcesz łatwości obsługi i kółek treningowych (aby uchronić Cię przed awarią bez konieczności uczenia się "właściwego" zarządzania pamięcią), wybierz coś z GC. Nawet jeśli wiesz, jak dobrze zarządzać pamięcią, zaoszczędzisz czas, który możesz poświęcić na optymalizację innego kodu. Naprawdę nie ma już wiele kary za wydajność, ale jeśli naprawdę potrzebujesz niezawodnego wydajność (i umiejętność dokładnego poznania co się dzieje, kiedy, pod przykryciem) wtedy zostałbym Przy C++. Nie bez powodu każdy główny silnik gry, o którym kiedykolwiek słyszałem, jest w C++ (jeśli nie C lub assembly). Python, et al są w porządku dla skryptów, ale nie główny silnik gry.

 204
Author: markets,
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
2017-05-23 12:18:18

Poniższe nie są oczywiście do końca precyzyjne. Weź to z przymrużeniem oka jak to przeczytasz:)

Cóż, trzy rzeczy, o których mówisz to automatyczny, statyczny i dynamiczny czas przechowywania , który ma coś wspólnego z tym, jak długo obiekty żyją i kiedy zaczynają życie.


Automatyczny czas przechowywania

Używasz automatycznego czasu przechowywania dla krótkotrwałych i małych danych, które są potrzebne tylko lokalnie w niektórych blok:

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

Życie kończy się, gdy tylko opuścimy blok, i rozpoczyna się, gdy tylko obiekt zostanie zdefiniowany. Są one najprostszym rodzajem czasu przechowywania i są znacznie szybsze niż w szczególności dynamiczny czas przechowywania.


Statyczny czas przechowywania

Używasz statycznego czasu przechowywania dla zmiennych wolnych, które mogą być dostępne przez dowolny kod przez cały czas, jeśli ich zakres pozwala na takie użycie (zakres przestrzeni nazw), oraz dla zmiennych lokalnych, które wymagają rozszerzenia ich czas życia w trakcie zakończenia ich zakresu (zakres lokalny) oraz dla zmiennych członkowskich, które muszą być współdzielone przez wszystkie obiekty ich klasy (zakres klas). Ich żywotność zależy od zakresu, w jakim się znajdują. Mogą mieć zakres przestrzeni nazw i Zakres lokalny oraz Zakres klasy. Co jest prawdą o obu z nich jest, gdy ich życie zaczyna, życie kończy się na koniec programu . Oto dwa przykłady:

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

Program drukuje ababab, ponieważ localA nie jest zniszczony po wyjściu z bloku. Można powiedzieć, że obiekty o zasięgu lokalnym rozpoczynają życie , gdy kontrola osiągnie swoją definicję . Dla localA, dzieje się, gdy ciało funkcji jest wprowadzane. W przypadku obiektów z zakresu przestrzeni nazw okres życia zaczyna się od uruchomienia programu . To samo dotyczy obiektów statycznych klasy scope:

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

Jak widzisz, classScopeA nie jest związana z konkretnymi obiektami swojej klasy, ale z samą klasą. Adres wszystkich trzech nazwisk powyżej jest taki sam, i wszystkie oznaczają ten sam obiekt. Istnieją specjalne reguły dotyczące czasu i sposobu inicjalizacji obiektów statycznych, ale nie przejmujmy się tym teraz. Oznacza to termin statyczny porządek inicjalizacji .


Dynamiczny czas przechowywania

Ostatni czas przechowywania jest dynamiczny. Używasz go, jeśli chcesz, aby obiekty mieszkały na innej wyspie i chcesz umieścić wskaźniki wokół nich. Używasz ich również, jeśli twoje obiekty są Duże , a jeśli chcesz utworzyć tablice o rozmiarze znanym tylko w czasie działania . Ze względu na tę elastyczność, obiekty o dynamicznym czasie przechowywania są skomplikowane i wolne w zarządzaniu. Obiekty o tak dynamicznym czasie trwania rozpoczynają życie, gdy zachodzi odpowiednie wywołanie nowego operatora:

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

Jego żywotność kończy się tylko wtedy, gdy zadzwonisz Usuń dla nich. Jeśli o tym zapomnisz, te przedmioty Nigdy nie kończą życia. I Obiekty klasy, które definiują konstruktor zadeklarowany przez użytkownika nie będą miały dzwonili ich Niszczyciele. Obiekty o dynamicznym czasie przechowywania wymagają ręcznej obsługi ich żywotności i powiązanych zasobów pamięci. Biblioteki istnieją po to, aby ułatwić korzystanie z nich. jawny zbiór śmieci dla poszczególnych obiektów można ustalić za pomocą inteligentnego wskaźnika:

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

Nie musisz przejmować się wywołaniem delete: współdzielony PST robi to za Ciebie, jeśli ostatni wskaźnik odwołujący się do obiektu wyjdzie poza zakres. Sam współdzielony PST posiada automatyczne czas przechowywania. Tak więc czas życia jest automatycznie zarządzany, pozwalając mu sprawdzić, czy powinien usunąć wskazywany na dynamiczny obiekt w swoim destruktorze. Aby uzyskać odniesienie shared_ptr, Zobacz dokumenty boost: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm

 51
Author: Johannes Schaub - litb,
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-11-02 20:27:56

Zostało powiedziane dokładnie, tak jak "krótka odpowiedź":

  • Zmienna statyczna (Klasa)
    lifetime = program runtime (1)
    widoczność = określana przez modyfikatory dostępu (prywatne/chronione/publiczne)

  • Zmienna statyczna (zakres globalny)
    lifetime = program runtime (1)
    visibility = Jednostka kompilacji, w której jest utworzona(2)

  • Zmienna sterty
    lifetime = defined by you (new to delete)
    widoczność = zdefiniowana przez Ciebie (niezależnie od tego, do czego przypisujesz wskaźnik)

  • Zmienna stosu
    visibility = from declaration until scope is exited
    lifetime = from declaration until declaring scope is exited


(1) dokĹ ' adniej: od inicjalizacji do deinicjalizacji jednostki kompilacji (tj. pliku C / C++). Kolejność inicjalizacji jednostek kompilacji nie jest określona przez standard.

(2) Strzeż się: jeśli tworzy instancję zmiennej statycznej w nagłówku, a każda jednostka kompilacji otrzymuje własną kopię.

 35
Author: peterchen,
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
2011-02-10 07:14:32

Jestem pewien, że jeden z pedantów wkrótce znajdzie lepszą odpowiedź, ale główną różnicą jest szybkość i rozmiar.

Stack

Znacznie szybszy przydział. Odbywa się to w O(1), ponieważ jest alokowany podczas ustawiania ramki stosu, więc jest zasadniczo wolny. Wadą jest to, że jeśli zabraknie miejsca na stosie jesteś kości. Możesz dostosować rozmiar stosu, ale IIRC masz ~2MB do zabawy. Ponadto, gdy tylko zakończysz funkcję, wszystko na stosie zostanie wyczyszczone. Więc to może być problematyczne, aby odnieść się do niego później. (Wskaźniki stosu przydzielonych obiektów prowadzą do błędów.)

Heap

Drastycznie wolniejszy przydział. Ale masz GB do zabawy i wskaż.

Garbage Collector

Garbage collector to jakiś kod, który działa w tle i zwalnia pamięć. Kiedy przydzielasz pamięć na stercie, bardzo łatwo jest zapomnieć o jej zwolnieniu, co jest znane jako wyciek pamięci. Z biegiem czasu pamięć zużywana przez aplikację rośnie i rośnie, aż rozbija się. Okresowe uwalnianie pamięci, której już nie potrzebujesz, pomaga wyeliminować tę klasę błędów. Oczywiście ma to swoją cenę, ponieważ Śmieciarz spowalnia wszystko.

 5
Author: Chris Smith,
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-01-03 06:06:29

Jakie są problemy statyczne i stack?

Problem z alokacją "statyczną" polega na tym, że alokacja jest dokonywana w czasie kompilacji: nie można jej użyć do przydzielenia jakiejś zmiennej liczby danych, której liczba nie jest znana do czasu uruchomienia.

Problem z alokacją na "stosie" polega na tym, że alokacja jest niszczona, gdy tylko powróci podprogram, który dokonuje alokacji.

Mógłbym napisać całą aplikację bez przydzielania zmienne w stercie?

Być może, ale nie nietrywialna, normalna, duża aplikacja (ale tak zwane "wbudowane" programy mogą być pisane bez sterty, używając podzbioru C++).

Co robi garbage collector ?

Obserwuje Twoje dane ("Oznacz i zamiataj"), aby wykryć, kiedy aplikacja nie odwołuje się już do nich. Jest to wygodne dla aplikacji, ponieważ aplikacja nie musi dealokować danych ... ale Śmieciarz może być kosztowne obliczeniowo.

Śmieciarki nie są standardową cechą programowania w C++.

Co mógłbyś zrobić samemu manipulując pamięcią, czego nie mógłbyś zrobić używając tego śmieciarza?

Poznaj mechanizmy deterministycznej dealokacji pamięci C++:

  • 'static': never deallocated
  • 'stos': gdy tylko zmienna "wyjdzie poza zakres"
  • 'sterta' : gdy wskaźnik zostanie usunięty (jawnie usunięty przez w tym celu należy skontaktować się z inspektorem ochrony danych.]}
 3
Author: ChrisW,
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-01-03 06:12:21

Alokacja pamięci stosu (zmienne funkcyjne, zmienne lokalne) może być problematyczna, gdy stos jest zbyt "głęboki" i przepełnia się pamięć dostępną do alokacji stosu. Sterta jest przeznaczona dla obiektów, które muszą być dostępne z wielu wątków lub przez cały cykl życia programu. Możesz napisać cały program bez użycia sterty.

Można łatwo wyciekać pamięć bez garbage collector, ale można również dyktować, kiedy przedmioty i pamięć są uwalniane. Wbiegłem do problemy z Javą podczas uruchamiania GC i mam proces w czasie rzeczywistym, ponieważ GC jest wyłącznym wątkiem (nic innego nie może działać). Więc jeśli wydajność jest krytyczna i możesz zagwarantować, że nie ma wycieków obiektów, nie używanie GC jest bardzo pomocne. W przeciwnym razie po prostu nienawidzisz życia, gdy aplikacja zużywa pamięć i musisz znaleźć źródło przecieku.

 1
Author: Rob Elsner,
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-01-03 06:09:58

Co jeśli twój program nie wie z góry ile pamięci ma przeznaczyć (stąd nie możesz użyć zmiennych stosu). Powiedzmy, że listy połączone, listy mogą rosnąć, nie wiedząc z góry, jaka jest ich wielkość. Tak więc przydzielanie na stertę ma sens dla połączonej listy, gdy nie wiesz, ile elementów zostanie do niej wstawionych.

 1
Author: kal,
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-01-03 06:36:24

Zaletą GC w niektórych sytuacjach jest irytacja w innych; poleganie na GC zachęca do nie myślenia o tym zbyt wiele. Teoretycznie czeka do "bezczynnego" okresu lub do momentu, w którym absolutnie musi, kiedy ukradnie przepustowość i spowoduje opóźnienie odpowiedzi w aplikacji.

Ale nie musisz o tym nie myśleć."Tak jak Wszystko inne w aplikacjach wielowątkowych, kiedy można ustąpić, można ustąpić. Tak więc na przykład w. Net można zażądać GC; robiąc to, zamiast rzadsze dłuższe uruchamianie GC, możesz mieć częstsze krótsze uruchamianie GC i rozłożyć opóźnienia związane z tym obciążeniem.

Ale to pokonuje główną atrakcję GC, która wydaje się być " zachęcana, aby nie myśleć o tym wiele, ponieważ jest to auto-mat-ic."

Jeśli po raz pierwszy zetknąłeś się z programowaniem, zanim GC stało się powszechne i czułeś się komfortowo z malloc / free i new / delete, to może nawet okazać się, że GC jest trochę irytujące i / lub są nieufne(jak można być nieufnym "optymalizacji", która miała sprawdzoną historię.) Wiele aplikacji toleruje losowe opóźnienia. Ale w przypadku aplikacji, które tego nie robią, gdzie losowe opóźnienia są mniej akceptowalne, powszechną reakcją jest unikanie środowisk GC i poruszanie się w kierunku czysto niezarządzanego kodu (lub broń Boże, dawno umierająca sztuka, język asemblacji.)

Jakiś czas temu miałem tu letniego studenta, stażystę, mądrego dzieciaka, który został odstawiony na GC; był tak zachwycony wyższością GC że nawet przy programowaniu w niezarządzanym C / C++ odmówił podążania za modelem malloc / free new / delete, ponieważ, cytuję, " nie powinieneś tego robić w nowoczesnym języku programowania."I wiesz? W przypadku małych, krótkich aplikacji można rzeczywiście ujść na sucho, ale nie w przypadku długotrwałych aplikacji wydajnych.

 1
Author: frediano,
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
2013-05-22 13:06:52

Stos jest pamięcią przydzieloną przez kompilator, kiedy kiedykolwiek kompilujemy program, w domyślnym kompilatorze przydziela trochę pamięci z systemu operacyjnego (Możemy zmienić ustawienia z ustawień kompilatora w IDE) i OS jest Tym, który daje pamięć, jego zależy od wielu dostępnych pamięci w systemie i wiele innych rzeczy, i przychodząc do pamięci stosu jest przydzielać, gdy deklarujemy zmienną kopiują (ref jako formals) te zmienne są pchane do stosu postępują niektóre konwencje nazewnictwa domyślnie its CDECL w Visual studios ex: notacja infiksowa: c=A + b; pchanie stosu odbywa się od prawej do lewej, pchanie b do stosu, operator, a do stosu i wynik tych i, e c do stosu. W notacji Pre fix: = + cab Tutaj wszystkie zmienne są wypychane do stosu 1st (od prawej do lewej), a następnie wykonywana jest operacja. Pamięć przydzielona przez kompilator jest stała. Załóżmy więc, że 1MB pamięci jest przypisane do naszej aplikacji, powiedzmy, że zmienne używane 700kb pamięci (wszystkie zmienne lokalne są wypychane do stosu, chyba że sÄ ... przydzielane dynamicznie) wiÄ ™ c pozostaĹ ' e 324kb pamiÄ ™ ci jest przydzielane do sterty. I ten stos ma mniej czasu życia, gdy zakres funkcji kończy te stosy zostanie wyczyszczone.

 0
Author: raj,
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
2013-11-06 04:50:32