Biorąc pod uwagę, że pamięć jest podzielona na cztery segmenty: dane, stertę, stos i kod, gdzie są zmienne globalne, zmienne statyczne, stałe typy danych, zmienne lokalne (zdefiniowane i zadeklarowane w funkcjach), zmienne (w funkcji głównej), wskaźniki i dynamicznie przydzielana przestrzeń (za pomocą malloc i calloc) zostaje zapisana w pamięci?
Myślę, że zostałyby przydzielone w następujący sposób:
- Zmienne globalne -------> dane
- Zmienne statyczne -------> dane
- Stałe typy danych -----> kod
- Zmienne lokalne (zadeklarowane i zdefiniowane w funkcjach) --------> stos
- Zmienne zadeklarowane i zdefiniowane w funkcji głównej -----> sterta
- Wskaźniki (na przykład
char *arr
,int *arr
) -------> sterta - Dynamicznie przydzielana przestrzeń (za pomocą malloc i calloc) --------> stos
Odnoszę się do tych zmiennych tylko z perspektywy C.
Proszę mnie poprawić, jeśli się mylę, ponieważ jestem nowy w C.
c
memory
memory-management
types
starkk92
źródło
źródło
main
to tylko kolejna funkcja. Zmienne trafiają na stos, chyba żemalloc
tak jak gdzie indziej.Odpowiedzi:
Niektóre z nich masz rację, ale ktokolwiek napisał pytania, oszukał cię co najmniej w jednym pytaniu:
main
funkcji ----->heaprównieżukładają się wstos (nauczyciel próbował cię oszukać)char *arr
,int *arr
) -------> danestertylub stosu, w zależności od kontekstu. C pozwala zadeklarować globalną lubstatic
wskaźnik, w którym to przypadku sam wskaźnik znalazłby się w segmencie danych.malloc
,calloc
,realloc
) -------->stoskupieWarto wspomnieć, że „stos” jest oficjalnie nazywany „klasą automatycznego przechowywania”.
źródło
alloca
funkcja, która działa podobnie domalloc
alokacji stosu, ale działa.Dla tych przyszłych gości, którzy mogą być zainteresowani poznaniem tych segmentów pamięci, piszę ważne punkty dotyczące 5 segmentów pamięci w C:
Niektóre ostrzeżenia:
5 segmentów pamięci w C:
1. Segment kodu
printf("Hello, world")
, w segmencie kodu / tekstu zostanie utworzony ciąg „Hello, world”. Możesz to zweryfikować za pomocąsize
polecenia w systemie operacyjnym Linux.Segment danych
Segment danych jest podzielony na dwie poniższe części i zazwyczaj znajduje się poniżej obszaru sterty lub w niektórych implementacjach powyżej stosu, ale segment danych nigdy nie leży między stertą a obszarem stosu.
2. Niezainicjowany segment danych
int globalVar;
lub statyczna zmienna lokalnastatic int localStatic;
będą przechowywane w niezainicjowanym segmencie danych.0
lubNULL
to nadal przechodzi do niezainicjowanego segmentu danych lub bss.3. Zainicjowany segment danych
int globalVar = 1;
lub statyczna zmienna lokalnastatic int localStatic = 1;
będzie przechowywana w zainicjowanym segmencie danych.4. Segment stosu
5. Segment sterty
malloc
,calloc
lubrealloc
metod.int* prt = malloc(sizeof(int) * 2)
wtedy osiem bajtów zostanie przydzielonych w stercie, a adres pamięci tej lokalizacji zostanie zwrócony i zapisany wptr
zmiennej. Plikptr
Zmienna będzie on albo segment stosu lub dane w zależności od sposobu ich deklarowanej / używane.źródło
Poprawione złe zdania
lokalne zmienne stałe -----> stos
zainicjowana globalna zmienna stała -----> segment danych
niezainicjowana globalna zmienna stała -----> bss
zmienne zadeklarowane i zdefiniowane w funkcji głównej -----> stos
wskaźniki (np: char * arr, int * arr) -------> rozmiar tej zmiennej wskaźnikowej będzie na stosie.
Weź pod uwagę, że przydzielasz pamięć o wielkości n bajtów (przy użyciu
malloc
lubcalloc
) dynamicznie, a następnie ustawiasz zmienną wskaźnika, aby ją wskazywała. Teraz, gdyn
bajty pamięci są w stercie, a zmienna wskaźnikowa wymaga 4 bajtów (jeśli maszyna 64-bitowa to 8 bajtów), które będą w stosie, aby przechowywać wskaźnik początkowyn
bajtów fragmentu pamięci.Uwaga: Zmienne wskaźnikowe mogą wskazywać pamięć dowolnego segmentu.
dynamicznie przydzielane miejsce (przy użyciu malloc, calloc) --------> heap
źródło
Popularna architektura pulpitu dzieli pamięć wirtualną procesu na kilka segmentów :
Segment tekstowy: zawiera kod wykonywalny. Wskaźnik instrukcji przyjmuje wartości z tego zakresu.
Segment danych: zawiera zmienne globalne (tj. Obiekty ze statycznym połączeniem). Podzielone na dane tylko do odczytu (takie jak stałe łańcuchowe) i niezainicjowane dane („BSS”).
Segment stosu: zawiera dynamiczną pamięć programu, tj. Wolną pamięć („stertę”) i lokalne ramki stosu dla wszystkich wątków. Tradycyjnie stos C i sterta C rosły do segmentu stosu z przeciwnych końców, ale uważam, że praktyka ta została porzucona, ponieważ jest zbyt niebezpieczna.
Program AC zazwyczaj umieszcza obiekty ze statycznym czasem trwania w segmencie danych, dynamicznie przydzielane obiekty w wolnym magazynie i obiekty automatyczne na stosie wywołań wątku, w którym żyje.
Na innych platformach, takich jak stary tryb rzeczywisty x86 lub na urządzeniach wbudowanych, sytuacja może się radykalnie różnić.
źródło
Z punktu widzenia języka C liczy się tylko zasięg, zakres, powiązania i dostęp; Dokładny sposób mapowania elementów do różnych segmentów pamięci zależy od indywidualnej implementacji i będzie się różnić. Standardowy język nie mówić o segmentach pamięci w ogóle . Większość współczesnych architektur działa w większości w ten sam sposób; zmienne o zasięgu blokowym i argumenty funkcji zostaną przydzielone ze stosu, zmienne o zasięgu plikowym i statyczne zostaną przydzielone z segmentu danych lub kodu, pamięć dynamiczna zostanie przydzielona ze sterty, niektóre dane stałe będą przechowywane w segmentach tylko do odczytu itp.
źródło
Jedną rzeczą, o której należy pamiętać w przypadku przechowywania, jest zasada as-if . Kompilator nie musi umieszczać zmiennej w określonym miejscu - zamiast tego może umieścić ją w dowolnym miejscu tak długo, jak długo skompilowany program zachowuje się tak, jakby był uruchamiany na abstrakcyjnej maszynie C zgodnie z regułami abstrakcyjnej maszyny C. Dotyczy to wszystkich okresów przechowywania . Na przykład:
42
w wygenerowanym kodzie asemblera, ale nie ma znaku404
.const
lub faktycznieconst
nie musi być w pamięci. Przykład - kompilator może udowodnić, żefoo
jest skutecznyconst
i wbudowuje jego użycie w kod.bar
ma łącze zewnętrzne i kompilator nie może udowodnić, że nie zostałby zmieniony poza bieżącym modułem, dlatego nie jest wstawiany.malloc
nie musi znajdować się w pamięci przydzielonej ze sterty! Przykład - zwróć uwagę, że kod nie ma wywołaniamalloc
i wartość 42 nie jest nigdy przechowywana w pamięci, jest przechowywana w rejestrze!malloc
i odniesienie jest tracony bez zwalniania obiektu bezfree
konieczności przecieku pamięci ...malloc
nie musi znajdować się w stercie poniżej programu break (sbrk(0)
) w systemie Unixen ...źródło
Nie, mogą znajdować się na stosie lub w segmencie danych. Mogą wskazywać w dowolnym miejscu.
źródło
main
zmiennych alokowanych dynamicznie również są błędneźródło
Minimalne uruchamialne przykłady w systemie Linux z analizą dezasemblacji
Ponieważ jest to szczegół implementacji, który nie jest określony przez standardy, przyjrzyjmy się tylko, co kompilator robi w określonej implementacji.
W tej odpowiedzi albo podam link do konkretnych odpowiedzi, które wykonują analizę, albo przedstawię analizę bezpośrednio tutaj i podsumuję wszystkie wyniki tutaj.
Wszystkie są w różnych wersjach Ubuntu / GCC, a wyniki są prawdopodobnie dość stabilne we wszystkich wersjach, ale jeśli znajdziemy jakieś różnice, określmy bardziej precyzyjne wersje.
Zmienna lokalna wewnątrz funkcji
Czy to
main
czy jakakolwiek inna funkcja:Jak pokazano na: Co oznacza <wartość zoptymalizowana na zewnątrz> w gdb?
-O0
: stos-O3
: rejestruje, jeśli się nie rozlewają, stosuj inaczejAby dowiedzieć się, dlaczego stos istnieje, zobacz: Jaka jest funkcja instrukcji push / pop używanych w rejestrach w asemblerze x86?
Zmienne globalne i
static
zmienne funkcyjne0
lub nie zainicjowano (a zatem niejawnie zainicjowano w0
):.bss
sekcja, zobacz także: Dlaczego segment .bss jest wymagany?.data
sekcjachar *
ichar c[]
Jak pokazano na: Gdzie są przechowywane zmienne statyczne w C i C ++?
DO ZROBIENIA, czy na stosie zostaną również umieszczone bardzo duże literały ciągów? Albo
.data
? A może kompilacja się nie udaje?Argumenty funkcji
Musi przejść przez odpowiednią konwencję telefoniczną, np .: https://en.wikipedia.org/wiki/X86_calling_conventions dla X86, która określa konkretne rejestry lub lokalizacje stosu dla każdej zmiennej.
Następnie, jak pokazano w Co oznacza <wartość zoptymalizowana na zewnątrz> w gdb? ,
-O0
a następnie wysysa wszystko do stosu,-O3
próbując jednocześnie używać rejestrów tak często, jak to możliwe.Jeśli jednak funkcja zostanie wstawiona, są traktowani tak, jak zwykli lokalni.
const
Uważam, że nie ma to znaczenia, ponieważ można to odrzucić.
I odwrotnie, jeśli kompilator jest w stanie określić, że niektóre dane nigdy nie są zapisywane, teoretycznie mógłby umieścić je w,
.rodata
nawet jeśli nie jest to const.Analiza TODO.
Wskaźniki
Są to zmienne (zawierające adresy, które są liczbami), tak samo jak cała reszta :-)
malloc
Pytanie nie ma większego sensu
malloc
, ponieważmalloc
jest funkcją, a w:*i
jest zmienną, która zawiera adres, więc przypada na powyższy przypadek.Jeśli chodzi o to, jak malloc działa wewnętrznie, kiedy to nazywasz, jądro Linuksa oznacza określone adresy jako zapisywalne w swoich wewnętrznych strukturach danych, a kiedy program dotyka ich początkowo, pojawia się błąd i jądro włącza tabele stron, co umożliwia dostęp dzieje się bez segfaul: Jak działa stronicowanie x86?
Zauważ jednak, że jest to w zasadzie dokładnie to, co
exec
wywołanie systemowe robi pod maską, kiedy próbujesz uruchomić plik wykonywalny: zaznacza strony, na które chce załadować, i zapisuje tam program, zobacz także: Jak jądro pobiera wykonywalny plik binarny działający pod linux? Z wyjątkiem tego, żeexec
ma pewne dodatkowe ograniczenia dotyczące miejsca, do którego należy załadować (np. Czy kodu nie można przenieść ).Dokładne wywołanie systemowe używane
malloc
jestmmap
we współczesnych implementacjach 2020, a w przeszłościbrk
było używane: Czy malloc () używa brk () lub mmap ()?Biblioteki dynamiczne
Zasadniczo pobierz się
mmap
do pamięci: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710envinroment zmienne i
main
sargv
Powyższy stos początkowy: /unix/75939/where-is-the-environment-string-actual-stored TODO, czemu nie w .data?
źródło