W jakim segmencie (.BSS, .DATA, inny) pliku wykonywalnego przechowywane są zmienne statyczne, aby nie powodowały kolizji nazw? Na przykład:
foo.c: bar.c:
static int foo = 1; static int foo = 10;
void fooTest() { void barTest() {
static int bar = 2; static int bar = 20;
foo++; foo++;
bar++; bar++;
printf("%d,%d", foo, bar); printf("%d, %d", foo, bar);
} }
Jeśli skompiluję oba pliki i połączę je z głównym, który wielokrotnie wywołuje fooTest () i barTest, instrukcje printf zwiększają się niezależnie. Ma to sens, ponieważ zmienne foo i bar są lokalne dla jednostki tłumaczeniowej.
Ale gdzie przydzielane jest miejsce?
Dla jasności założono, że masz zestaw narzędzi, który wyprowadzałby plik w formacie ELF. Dlatego uważam , że w pliku wykonywalnym musi być trochę miejsca zarezerwowanego dla tych zmiennych statycznych.
Dla celów dyskusji załóżmy, że używamy łańcucha narzędzi GCC.
c++
c
compiler-construction
Benoit
źródło
źródło
Odpowiedzi:
To, dokąd zmierza Twoja statystyka, zależy od tego, czy są one inicjowane zerem . dane statyczne zainicjowane zerem idą do .BSS (Block Started by Symbol) , dane niezainicjalizowane zerem idą do .DATA
źródło
.data
, oraz dane statyczne bez inicjatora.bss
.Po załadowaniu programu do pamięci jest on podzielony na różne segmenty. Jednym z segmentów jest segment danych . Segment danych jest dalej podzielony na dwie części:
Zainicjowany segment danych: Tutaj przechowywane są wszystkie dane globalne, statyczne i stałe.
Niezainicjowany segment danych (BSS): Wszystkie niezainicjowane dane są przechowywane w tym segmencie.
Oto schemat wyjaśniający tę koncepcję:
tutaj jest bardzo dobry link wyjaśniający te pojęcia:
źródło
W rzeczywistości zmienną jest krotka (pamięć, zakres, typ, adres, wartość):
Zasięg lokalny może oznaczać lokalny dla jednostki translacyjnej (pliku źródłowego), funkcji lub bloku, w zależności od tego, gdzie jest zdefiniowany. Aby zmienna była widoczna dla więcej niż jednej funkcji, na pewno musi znajdować się w obszarze DANYCH lub BSS (w zależności od tego, czy odpowiednio została zainicjowana jawnie, czy nie). Następnie jest odpowiednio dopasowywany do wszystkich funkcji lub funkcji w pliku źródłowym.
źródło
Miejsce przechowywania danych będzie zależało od implementacji.
Jednak znaczenie statyczne to „powiązanie wewnętrzne”. Tak więc symbol jest wewnętrzny dla jednostki kompilacji (foo.c, bar.c) i nie można się do niego odwoływać poza tą jednostką kompilacji. Tak więc nie może być kolizji nazw.
źródło
w obszarze „globalnym i statycznym” :)
W C ++ istnieje kilka obszarów pamięci:
Zobacz tutaj szczegółową odpowiedź na swoje pytanie:
źródło
Nie wierzę, że nastąpi kolizja. Użycie statycznego na poziomie pliku (funkcje zewnętrzne) oznacza zmienną jako lokalną dla bieżącej jednostki kompilacji (pliku). Nigdy nie jest widoczny poza bieżącym plikiem, więc nigdy nie musi mieć nazwy, której można używać zewnętrznie.
Używanie statycznego wewnątrz funkcji jest inne - zmienna jest widoczna tylko dla funkcji (statycznej lub nie), jej wartość jest zachowywana w wywołaniach tej funkcji.
W efekcie statyczny robi dwie różne rzeczy w zależności od tego, gdzie się znajduje. W obu przypadkach widoczność zmiennych jest jednak ograniczona w taki sposób, że można łatwo zapobiec konfliktom przestrzeni nazw podczas łączenia.
Powiedziawszy to, wierzę, że będzie przechowywany w
DATA
sekcji, która zwykle ma zmienne, które są inicjowane na wartości inne niż zero. Jest to oczywiście szczegół implementacji, a nie coś nakazanego przez standard - zależy tylko na zachowaniu, a nie na tym , jak rzeczy są wykonywane pod przykryciem.źródło
So, how does a segment of memory (Data Segment) store variables that can be accessed from everywhere (global variables) and also those which have limited scope (file scope or function scope in case of static variables)?
Jak samemu to znaleźć
objdump -Sr
Aby faktycznie zrozumieć, co się dzieje, musisz zrozumieć relokację linkera. Jeśli nigdy tego nie dotykałeś, najpierw przeczytaj ten post .
Przeanalizujmy przykład ELF Linux x86-64, aby zobaczyć go sami:
Połącz z:
Dekompiluj kod za pomocą:
-S
dekompiluje kod z oryginalnym pomieszanym źródłem-r
pokazuje informacje o relokacjiWewnątrz dekompilacji
f
widzimy:i
.data-0x4
mówi, że przejdzie do pierwszego bajtu.data
segmentu.-0x4
Jest tam, ponieważ używamy RIP Adresowanie względne, więc%rip
w instrukcji iR_X86_64_PC32
.Jest to wymagane, ponieważ RIP wskazuje na następującą instrukcję, która rozpoczyna 4 bajty, po
00 00 00 00
których następuje przeniesienie. Wyjaśniłem to bardziej szczegółowo na stronie : https://stackoverflow.com/a/30515926/895245Następnie, jeśli zmodyfikujemy źródło
i = 1
i wykonamy tę samą analizę, stwierdzimy, że:static int i = 0
trwa.bss
static int i = 1
trwa.data
źródło
Oto jak (łatwo zrozumieć):
źródło
To zależy od używanej platformy i kompilatora. Niektóre kompilatory przechowują bezpośrednio w segmencie kodu. Zmienne statyczne są zawsze dostępne tylko dla bieżącej jednostki tłumaczeniowej, a nazwy nie są eksportowane, dlatego kolizje nazw nigdy nie występują.
źródło
Dane zadeklarowane w jednostce kompilacyjnej trafią do .BSS lub .Data danych wyjściowych tych plików. Zainicjowane dane w BSS, niezainicjowane w DANYCH.
Różnica między danymi statycznymi i globalnymi polega na włączeniu informacji o symbolu do pliku. Kompilatory zwykle zawierają informacje o symbolach, ale jako takie zaznaczają tylko informacje globalne.
Linker szanuje tę informację. Informacje o symbolach dla zmiennych statycznych są odrzucane lub zniekształcane, dzięki czemu do zmiennych statycznych można nadal odwoływać się w jakiś sposób (za pomocą opcji debugowania lub symboli). W żadnym przypadku nie ma to wpływu na jednostki kompilacji, ponieważ linker najpierw rozpoznaje odniesienia lokalne.
źródło
Próbowałem z objdump i gdb, oto wynik, co otrzymuję:
oto wynik objdump
To znaczy, że twoje cztery zmienne znajdują się w sekcji danych o tej samej nazwie, ale z różnym przesunięciem.
źródło
zmienna statyczna przechowywana w segmencie danych lub segmencie kodu, jak wspomniano wcześniej.
Możesz być pewien, że nie zostanie on przydzielony na stosie lub stosie.
Nie ma ryzyka kolizji, ponieważ
static
słowo kluczowe definiuje zakres zmiennej, która ma być plikiem lub funkcją, w przypadku kolizji istnieje kompilator / linker, który Cię ostrzega.Ładny przykład
źródło
Cóż, to pytanie jest trochę za stare, ale ponieważ nikt nie wskazuje żadnych użytecznych informacji: sprawdź post przez „mohit12379” wyjaśniający magazyn zmiennych statycznych o tej samej nazwie w tabeli symboli: http://www.geekinterview.com/question_details/ 24745
źródło
Odpowiedź może bardzo zależeć od kompilatora, więc prawdopodobnie chcesz edytować swoje pytanie (to znaczy, nawet pojęcie segmentów nie jest wymagane przez ISO C ani ISO C ++). Na przykład w systemie Windows plik wykonywalny nie nosi nazw symboli. Jeden „foo” byłby odsunięty 0x100, drugi być może 0x2B0, a kod z obu jednostek tłumaczeniowych jest kompilowany znając przesunięcia dla „ich” foo.
źródło
oba będą przechowywane niezależnie, jednak jeśli chcesz to wyjaśnić innym programistom, możesz zawinąć je w przestrzenie nazw.
źródło
wiesz już, że albo przechowuje w bss (blok zaczyna się od symbolu) zwany także niezainicjowanym segmentem danych lub w zainicjowanym segmencie danych.
weźmy prosty przykład
powyższa zmienna statyczna nie jest inicjowana, więc trafia do niezainicjowanego segmentu danych (bss).
i oczywiście został zainicjowany przez 10, więc przechodzi do zainicjowanego segmentu danych.
źródło