Dlaczego książki mówią: „kompilator przydziela miejsce na zmienne w pamięci”. Czy nie jest to plik wykonywalny, który to robi? Mam na przykład na myśli, jeśli napiszę następujący program,
#include <iostream>
using namespace std;
int main()
{
int foo;
return 0;
}
i skompiluj go i uzyskaj plik wykonywalny (niech to będzie program.exe), teraz, jeśli uruchomię program.exe, ten plik wykonywalny sam poleci przydzielenie miejsca dla zmiennej foo. Prawda? Wyjaśnij, dlaczego książki wciąż mówią: „kompilator to zrobi ... zrób to”.
sizeof
Pytanie znajduje się teraz na Dlaczego sizeof nazywamy operator kompilacji?Odpowiedzi:
Masz rację, że kompilator jako taki zniknął, gdy twój program faktycznie uruchomił się. A jeśli działa na innym komputerze, kompilator nie jest już nawet dostępny.
Myślę, że ma to na celu wyraźne rozróżnienie między pamięcią faktycznie przydzieloną przez twój własny kod. Kompilator wstawi kod do twojego programu, który dokonuje alokacji pamięci (np. Używając nowych, malloc lub podobnych poleceń).
Więc książki używają „kompilator robi to lub tamto” często mówiąc, że kompilator dodał kod, który nie jest wyraźnie wymieniony w twoich plikach kodu. To prawda, że nie do końca tak się dzieje. Z tego punktu widzenia wiele rzeczy wymienionych w samouczkach byłoby błędnych, ale wymagałoby raczej szczegółowych wyjaśnień.
źródło
malloc
et. glin.To zależy od zmiennej. System operacyjny przydziela stertę, program przydzieli stos, a kompilator przydzieli miejsce dla globałów / statyki, tj. Są one wbudowane w sam plik exe. Jeśli przydzielisz 1 MB pamięci globalnej, rozmiar exe wzrośnie o co najmniej 1 MB
źródło
int test[256][1024]; int main(){ test[0][0]=2; return 0; }
ten mały program ma przydzielony 1 MB, ale generuje tylko plik obiektowy o wielkości 1,4 Kb i plik wykonywalny o wielkości 8,4 Kb. Powinien jednak używać prawidłowej ilości pamięci RAM.int a1=1,a2=2,
... aż do ..., a1048576=1048576;
Tylko wtedy zdecydowanie dostaniesz coś większego niż 1mb.co kompilator będzie zrobić, to wziąć swój kod i skompilować go do kodu maszynowego. To, co wspominasz, jest dobrym przykładem, w którym kompilator musi tylko tłumaczyć.
Na przykład kiedy piszesz
Możesz to zobaczyć, gdy „Mówię kompilatorowi, aby [ w danych wyjściowych, które generuje ] zażądał od komputera zarezerwowania wystarczającej ilości pamięci RAM na int, do którego będę mógł później się odwoływać Kompilator prawdopodobnie użyje identyfikatora zasobu lub jakiegoś mechanizmu do śledzenia foo kod maszynowy, zamiast pisać asembler, możesz użyć foo w pliku tekstowym! Hurra !
Możesz więc spojrzeć na to, ponieważ kompilator pisze list ( lub może powieść / encyklopedię ) do wszystkich docelowych procesorów i urządzeń. List jest zapisywany w postaci sygnałów binarnych, które (ogólnie) mogą być tłumaczone na różne procesory poprzez zmianę celu. Każda „litera” i / lub kombinacja może wysyłać różnego rodzaju żądania i / lub dane - na przykład proszę przydzielić miejsce dla tej zmiennej, z której korzystał programista.
źródło
Powiedzenie „kompilator alokuje pamięć” może nie być faktyczne w dosłownym tego słowa znaczeniu, ale jest to metafora, która sugeruje we właściwy sposób.
Tak naprawdę dzieje się tak, że kompilator tworzy program, który przydziela własną pamięć. Tyle że to nie program przydziela pamięć, ale system operacyjny.
Tak naprawdę dzieje się tak, że kompilator tworzy program opisujący wymagania dotyczące pamięci, a system operacyjny bierze ten opis i używa go do przydzielania pamięci. Poza tym, że system operacyjny jest programem, a programy w rzeczywistości nic nie robią, opisują obliczenia wykonywane przez procesor. Tyle że procesor to tak naprawdę skomplikowany obwód elektroniczny, a nie antropomorficzny mały homonculus.
Ale sensowne jest myślenie o programach, kompilatorach i procesorach jako o małych ludziach mieszkających w komputerze, nie dlatego, że tak naprawdę są, ale dlatego, że jest to metafora, która dobrze pasuje do ludzkiego mózgu.
Niektóre metafory działają dobrze do opisywania rzeczy na jednym poziomie abstrakcji, ale nie działają również na innym poziomie. Jeśli myślisz na poziomie kompilatora, sensowne jest opisanie procesu generowania kodu, który spowoduje przydzielenie pamięci podczas kompilacji programu w rzeczywistości jako „alokację pamięci”. Jest na tyle blisko, że kiedy myślimy o tym, jak działa kompilator, mamy właściwy pomysł i nie jest tak długo rozwikłany, że zapominamy o tym, co robiliśmy. Jeśli spróbujemy użyć tej metafory na poziomie uruchomionego kompilowanego programu, wprowadzi to w dziwny sposób, co zauważyłeś.
źródło
To kompilator decyduje, gdzie przechowywać zmienną - może znajdować się w stosie lub w wolnym rejestrze. Niezależnie od decyzji magazynu podjętej przez kompilator, wygenerowany zostanie odpowiedni kod maszynowy umożliwiający dostęp do tej zmiennej i nie będzie można jej zmienić w czasie wykonywania. W tym sensie kompilator odpowiada za przydzielanie miejsca na zmienne, a końcowy program.exe po prostu ślepo działa w czasie wykonywania jak zombie.
Teraz nie myl tego z innym dynamicznym zarządzaniem pamięcią, takim jak malloc, nowy lub może to być własne zarządzanie pamięcią. Kompilatory zajmują się zmiennym przechowywaniem i dostępem, ale nie ma znaczenia, co rzeczywista wartość oznacza w innym frameworku / bibliotece. Na przykład:
W czasie wykonywania malloc może zwrócić dowolną liczbę, ale kompilator nie dba o to, ważne jest tylko, gdzie ją zapisać.
źródło
Bardziej dokładnym sformułowaniem byłoby: - „kompilator mówi modułowi ładującemu, aby zarezerwował miejsce dla zmiennych”
W środowisku C będą istniały trzy typy przestrzeni dla zmiennych: -
W nowoczesnym systemie operacyjnym pamięć sterty nie będzie faktycznie zarezerwowana, ale przydzielana zgodnie z wymaganiami.
źródło
Tak, masz rację, w tym przypadku (deklarowanie zmiennej w funkcji) zdanie twojej książki jest prawdopodobnie nieprawidłowe: kiedy deklarujesz zmienną w funkcji, zostaje ona przydzielona na stosie po wejściu do funkcji. W każdym razie kompilator powinien zoptymalizować sytuację: jeśli funkcja nie jest rekurencyjna (
main()
jest dobrym kandydatem do niej), można „przydzielić” jej czas kompilacji (w BSS).(Jeśli jesteś ciekawy, gdzie znajdują się twoje zmienne, możesz to sprawdzić w nieprzyzwoity sposób (jeśli mimo to nie chcesz zbadać struktury pliku obj, dlaczego nie?), Możesz zadeklarować różne rodzaje zmiennych: stała, statyczne, dynamiczne,
malloc()
-alokowane itp. i wyświetlają ich adresy (użyj%X
formateraprintf()
dla lepszej czytelności). Zmienne znajdujące się na stosie będą miały bardzo różne adresy pamięci.)źródło
Jedyną rzeczą wykonaną w czasie wykonywania będzie podbijanie stosu o określoną wartość. Tak więc kompilator decyduje wcześniej:
Można to nazwać „alokacją”, ale oczywiście w czasie kompilacji zajmuje miejsce tylko w modelu, który posiada kompilator uruchomionego programu.
źródło