In welchem Segment (.BSS, .DATA, other) einer ausführbaren Datei werden statische Variablen gespeichert, damit sie keine Namenskollision haben? Beispielsweise:
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);
} }
Wenn ich beide Dateien kompiliere und sie mit einer Hauptdatei verknüpfe, die wiederholt fooTest () und barTest aufruft, werden die printf-Anweisungen unabhängig voneinander erhöht. Sinnvoll, da die Variablen foo und bar lokal für die Übersetzungseinheit sind.
Aber wo ist der Speicher zugeordnet?
Um klar zu sein, wird davon ausgegangen, dass Sie eine Toolchain haben, die eine Datei im ELF-Format ausgeben würde. So ich glaube , dass es hat etwas Platz in der ausführbaren Datei für die statischen Variablen reserviert werden.
Nehmen wir zu Diskussionszwecken an, wir verwenden die GCC-Toolchain.
quelle
Antworten:
Wohin Ihre Statik geht, hängt davon ab, ob sie mit Null initialisiert ist . Null-initialisierte statische Daten gehen in .BSS (Block Started by Symbol) , Nicht-Null-initialisierte Daten gehen in .DATA
quelle
.data
, und statische Daten ohne Initialisierer eingegeben wurden.bss
.Wenn ein Programm in den Speicher geladen wird, ist es in verschiedene Segmente unterteilt. Eines der Segmente ist das DATA-Segment . Das Datensegment ist weiter in zwei Teile unterteilt:
Initialisiertes Datensegment: Hier werden alle globalen, statischen und konstanten Daten gespeichert.
Nicht initialisiertes Datensegment (BSS): Alle nicht initialisierten Daten werden in diesem Segment gespeichert.
Hier ist ein Diagramm, um dieses Konzept zu erklären:
Hier ist ein sehr guter Link, der diese Konzepte erklärt:
quelle
Tatsächlich ist eine Variable ein Tupel (Speicher, Bereich, Typ, Adresse, Wert):
Lokaler Bereich kann lokal für die Übersetzungseinheit (Quelldatei), die Funktion oder den Block sein, je nachdem, wo er definiert ist. Um eine Variable für mehr als eine Funktion sichtbar zu machen, muss sie sich definitiv entweder im DATA- oder im BSS-Bereich befinden (abhängig davon, ob sie explizit initialisiert wurde oder nicht). Es wird dann entsprechend entweder allen Funktionen oder Funktionen in der Quelldatei zugeordnet.
quelle
Der Speicherort der Daten ist implementierungsabhängig.
Die Bedeutung von statisch ist jedoch "interne Verknüpfung". Daher befindet sich das Symbol innerhalb der Kompilierungseinheit (foo.c, bar.c) und kann außerhalb dieser Kompilierungseinheit nicht referenziert werden. Es kann also keine Namenskollisionen geben.
quelle
im Bereich "global und statisch" :)
In C ++ gibt es mehrere Speicherbereiche:
Sehen Sie hier für eine detaillierte Antwort auf Ihre Frage:
quelle
Ich glaube nicht, dass es zu einer Kollision kommen wird. Durch die Verwendung von static auf Dateiebene (externe Funktionen) wird die Variable als lokal für die aktuelle Kompilierungseinheit (Datei) markiert. Es ist außerhalb der aktuellen Datei niemals sichtbar und muss daher niemals einen Namen haben, der extern verwendet werden kann.
Die Verwendung von static innerhalb einer Funktion ist anders - die Variable ist nur für die Funktion sichtbar (ob statisch oder nicht), nur ihr Wert bleibt bei Aufrufen dieser Funktion erhalten.
In der Tat macht statische zwei verschiedene Dinge, je nachdem, wo es ist. In beiden Fällen ist die Sichtbarkeit der Variablen jedoch so eingeschränkt, dass Sie beim Verknüpfen leicht Namespace-Konflikte vermeiden können.
Trotzdem glaube ich, dass es in dem
DATA
Abschnitt gespeichert wird, der dazu neigt, Variablen zu haben, die mit anderen Werten als Null initialisiert werden. Dies ist natürlich ein Implementierungsdetail, das nicht vom Standard vorgeschrieben wird - es geht nur um das Verhalten, nicht darum , wie die Dinge unter der Decke gemacht werden.quelle
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)?
Wie man es selbst findet
objdump -Sr
Um wirklich zu verstehen, was los ist, müssen Sie die Verlagerung von Linkern verstehen. Wenn Sie das noch nie angerührt haben, lesen Sie zuerst diesen Beitrag .
Lassen Sie uns ein Linux x86-64 ELF-Beispiel analysieren, um es selbst zu sehen:
Kompilieren mit:
Dekompilieren Sie den Code mit:
-S
dekompiliert den Code mit der ursprünglichen Quelle vermischt-r
zeigt UmzugsinformationenIn der Dekompilierung von sehen
f
wir:und das
.data-0x4
sagt, dass es zum ersten Byte des.data
Segments gehen wird.Das
-0x4
ist da, weil wir die relative RIP-Adressierung verwenden, also die%rip
in der Anweisung undR_X86_64_PC32
.Dies ist erforderlich, da RIP auf die folgende Anweisung verweist , die 4 Bytes startet und danach
00 00 00 00
verschoben wird. Ich habe dies ausführlicher unter https://stackoverflow.com/a/30515926/895245 erklärtWenn wir dann die Quelle ändern
i = 1
und dieselbe Analyse durchführen, schließen wir Folgendes:static int i = 0
geht weiter.bss
static int i = 1
geht weiter.data
quelle
So (leicht zu verstehen):
quelle
Dies hängt von der Plattform und dem Compiler ab, die Sie verwenden. Einige Compiler speichern direkt im Codesegment. Statische Variablen sind immer nur für die aktuelle Übersetzungseinheit zugänglich und die Namen werden nicht exportiert, sodass die Kollisionen von Namensnamen niemals auftreten.
quelle
In einer Kompilierungseinheit deklarierte Daten werden in die Ausgabe von .BSS oder .Data dieser Dateien übertragen. Initialisierte Daten in BSS, nicht initialisiert in DATA.
Der Unterschied zwischen statischen und globalen Daten besteht in der Aufnahme von Symbolinformationen in die Datei. Compiler enthalten in der Regel die Symbolinformationen, markieren jedoch nur die globalen Informationen als solche.
Der Linker respektiert diese Informationen. Die Symbolinformationen für die statischen Variablen werden entweder verworfen oder entstellt, sodass auf statische Variablen weiterhin auf irgendeine Weise verwiesen werden kann (mit Debug- oder Symboloptionen). In keinem Fall können die Kompilierungseinheiten betroffen sein, da der Linker zuerst lokale Referenzen auflöst.
quelle
Ich habe es mit objdump und gdb versucht, hier ist das Ergebnis, was ich bekomme:
Hier ist das objdump-Ergebnis
Das heißt, Ihre vier Variablen befinden sich in Datenabschnittereignissen mit demselben Namen, jedoch mit unterschiedlichem Versatz.
quelle
statische Variable, die wie zuvor erwähnt im Datensegment oder Codesegment gespeichert ist.
Sie können sicher sein, dass es nicht auf Stapel oder Heap zugewiesen wird.
Es besteht kein Kollisionsrisiko, da
static
Schlüsselwörter den Bereich der Variablen als Datei oder Funktion definieren. Im Falle einer Kollision gibt es einen Compiler / Linker, vor dem Sie gewarnt werden können.Ein schönes Beispiel
quelle
Nun, diese Frage ist etwas zu alt, aber da niemand auf nützliche Informationen hinweist: Überprüfen Sie den Beitrag von 'mohit12379', in dem der gleichnamige Speicher statischer Variablen in der Symboltabelle erläutert wird: http://www.geekinterview.com/question_details/ 24745
quelle
Die Antwort hängt möglicherweise vom Compiler ab, daher möchten Sie wahrscheinlich Ihre Frage bearbeiten (ich meine, auch der Begriff der Segmente wird weder von ISO C noch von ISO C ++ vorgeschrieben). Unter Windows enthält eine ausführbare Datei beispielsweise keine Symbolnamen. Ein 'foo' wäre Offset 0x100, das andere vielleicht 0x2B0, und Code aus beiden Übersetzungseinheiten wird unter Kenntnis der Offsets für "ihr" foo kompiliert.
quelle
Sie werden beide unabhängig voneinander gespeichert. Wenn Sie jedoch anderen Entwicklern klar machen möchten, möchten Sie sie möglicherweise in Namespaces einbinden.
quelle
Sie wissen bereits, dass es entweder in bss (Blockstart durch Symbol), auch als nicht initialisiertes Datensegment bezeichnet, oder im initialisierten Datensegment gespeichert wird.
Nehmen wir ein einfaches Beispiel
Die obige statische Variable wird nicht initialisiert und geht daher in das nicht initialisierte Datensegment (bss).
und natürlich wird es um 10 initialisiert, so dass es zum initialisierten Datensegment geht.
quelle