Wo werden statische Variablen in C und C++ gespeichert?
Lesezeit: 10 Minuten
Benoit
In welchem Segment (.BSS, .DATA, andere) einer ausführbaren Datei werden statische Variablen gespeichert, damit sie keine Namenskollision haben? Zum Beispiel:
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 verlinke, die wiederholt fooTest() und barTest aufruft, werden die printf-Anweisungen unabhängig voneinander erhöht. Das ist sinnvoll, da die Variablen foo und bar lokal für die Übersetzungseinheit sind.
Aber wo wird der Speicher zugewiesen?
Um es klar zu sagen, die Annahme ist, dass Sie eine Toolchain haben, die eine Datei im ELF-Format ausgeben würde. Also ich glauben das da hat Platz in der ausführbaren Datei für diese statischen Variablen reserviert werden.
Nehmen wir zu Diskussionszwecken an, dass wir die GCC-Toolchain verwenden.
Die meisten Leute sagen Ihnen, dass sie im Abschnitt .DATA gespeichert werden sollten, anstatt Ihre Frage zu beantworten: Wo genau im Abschnitt .DATA und wie finden Sie wo. Ich sehe, Sie haben bereits eine Antwort markiert, also wissen Sie bereits, wie Sie sie finden?
– Lukmac
20. März 2011 um 20:50 Uhr
warum initialisiert und nicht initialisiert in verschiedenen Abschnitten platziert werden: linuxjournal.com/article/1059
– mhk
26. Juli 2012 um 16:39 Uhr
Der Speicherplatz, der Ihren globalen/statischen Variablen zur Laufzeit zugewiesen wird, hat nichts mit ihrer Namensauflösung zu tun, die während der Build-/Link-Zeit erfolgt. Nachdem die ausführbare Datei erstellt wurde, gibt es keine Namen mehr.
– Valdo
13. Oktober 2014 um 17:50 Uhr
Diese Frage ist bedeutungslos, da sie auf der falschen Prämisse basiert, dass “Namenskollision” von nicht exportierten Symbolen eine Sache sein kann, die existieren kann. Die Tatsache, dass es keine legitime Frage gibt, könnte erklären, wie schlimm einige der Antworten sind. Es ist schwer zu glauben, dass so wenige Leute das verstanden haben.
– Unterstrich_d
24. Juli 2016 um 22:26 Uhr
Don Neufeld
Wohin Ihre Statik geht, hängt davon ab, ob sie es sind Null initialisiert. Null initialisiert Statische Daten gehen ein .BSS (Block gestartet durch Symbol), nicht null initialisiert Daten gehen rein .DATEN
Mit “Nicht-0 initialisiert” meinen Sie wahrscheinlich “Initialisiert, aber mit etwas anderem als 0”. Weil es in C/C++ keine “nicht initialisierten” statischen Daten gibt. Alles Statische wird standardmäßig mit Null initialisiert.
– Ant
19. Juni 2010 um 9:33 Uhr
@Don Neufeld: Ihre Antwort beantwortet die Frage überhaupt nicht. Ich verstehe nicht, warum es akzeptiert wird. Weil sowohl ‘foo’ als auch ‘bar’ nicht mit 0 initialisiert sind. Die Frage ist, wo zwei statische/globale Variablen mit demselben Namen in .bss oder .data platziert werden sollen
– Lukmac
20. März 2011 um 20:42 Uhr
Ich habe Implementierungen verwendet, bei denen explizit nullinitialisierte statische Daten eingegeben wurden .dataund statische Daten ohne Initialisierer gingen ein .bss .
– MM
17. August 2014 um 6:22 Uhr
@MM In meinem Fall, ob das statische Mitglied nicht initialisiert (implizit auf 0 initialisiert) oder explizit auf 0 initialisiert ist, wird es in beiden Fällen im Abschnitt .bss summiert.
– Binder
5. September 2015 um 4:25 Uhr
Sind diese Informationen spezifisch für einen bestimmten ausführbaren Dateityp? Ich nehme an, da Sie nicht angegeben haben, dass dies zumindest für ausführbare ELF- und Windows PE-Dateien gilt, aber was ist mit anderen Typen?
– Jerry Jeremia
6. April 2020 um 2:57 Uhr
Karn
Wenn ein Programm in den Speicher geladen wird, ist es in verschiedene Segmente organisiert. Eines der Segmente ist 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:
Die obige Antwort besagt, dass 0 initialisiert in BSS geht. Bedeutet 0 initialisiert nicht initialisiert oder 0 per se? Wenn es per se 0 bedeutet, sollten Sie es meiner Meinung nach in Ihre Antwort aufnehmen.
– Viraj
22. Oktober 2015 um 2:37 Uhr
Konstante Daten werden nicht im .data-Segment, sondern im .const-Segment des Textabschnitts gespeichert.
– Benutzer10678
1. November 2017 um 4:34 Uhr
Beachten Sie auch, dass “initialisierte Daten” meines Wissens aus initialisiert bestehen können VariablenundKonstanten. Auf einem Mikrocontroller (zB: STM32), Initialisierte Variablen werden standardmäßig in gespeichert Blinken Gedächtnis und beim Start in den Arbeitsspeicher kopiertund initialisierte Konstanten sind darin belassen und sollen gelesen werden, Nur Blitzzusammen mit Textdie das Programm selbst enthält, und wird in belassen Nur Blitz.
– Gabriel Staples
5. November 2018 um 21:46 Uhr
Link ist kaputt 🙁
– subtiler Sucher
28. Februar 2020 um 6:59 Uhr
+1 für @GabrielStaples für die Hervorhebung der Tatsache, dass initialisierte Daten weiter in schreibgeschützt (=> .rodata-Abschnitt) und schreibgeschützt (=> .data-Abschnitt) klassifiziert werden können.
– ZeZNiQ
27. Januar um 17:57 Uhr
Tatsächlich ist eine Variable ein Tupel (Speicher, Gültigkeitsbereich, Typ, Adresse, Wert):
storage : where is it stored, for example data, stack, heap...
scope : who can see us, for example global, local...
type : what is our type, for example int, int*...
address : where are we located
value : what is our value
Lokaler Gültigkeitsbereich kann lokal für entweder die Übersetzungseinheit (Quelldatei), die Funktion oder den Block bedeuten, je nachdem, wo sie 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 (je nachdem, ob sie explizit initialisiert wurde oder nicht). Es wird dann entsprechend entweder auf alle Funktionen oder Funktionen in der Quelldatei beschränkt.
+1 für gründliche Kategorisierung auf hohem Niveau. Es wäre toll, wenn Sie auch auf die Quelle(n) dieser Informationen hinweisen könnten.
– ZeZNiQ
27. Januar um 17:59 Uhr
Der Speicherort der Daten ist implementierungsabhängig.
Allerdings ist die Bedeutung von statisch ist “interne Verknüpfung”. Somit ist das Symbol intern zur Kompilierungseinheit (foo.c, bar.c) und kann nicht außerhalb dieser Kompilierungseinheit referenziert werden. Es kann also keine Namenskollisionen geben.
ugasoft
im Bereich “global und statisch” 🙂
In C++ gibt es mehrere Speicherbereiche:
Haufen
kostenlos speichern
Stapel
global & statisch
konst
Sehen Hier für eine ausführliche antwort auf deine frage:
Im Folgenden werden die wichtigsten unterschiedlichen Speicherbereiche eines C++-Programms zusammengefasst. Beachten Sie, dass einige der Namen (z. B. “Haufen”) im Entwurf nicht als solche erscheinen [standard].
Memory Area Characteristics and Object Lifetimes
-------------- ------------------------------------------------
Const Data The const data area stores string literals and
other data whose values are known at compile
time. No objects of class type can exist in
this area. All data in this area is available
during the entire lifetime of the program.
Further, all of this data is read-only, and the
results of trying to modify it are undefined.
This is in part because even the underlying
storage format is subject to arbitrary
optimization by the implementation. For
example, a particular compiler may store string
literals in overlapping objects if it wants to.
Stack The stack stores automatic variables. Typically
allocation is much faster than for dynamic
storage (heap or free store) because a memory
allocation involves only pointer increment
rather than more complex management. Objects
are constructed immediately after memory is
allocated and destroyed immediately before
memory is deallocated, so there is no
opportunity for programmers to directly
manipulate allocated but uninitialized stack
space (barring willful tampering using explicit
dtors and placement new).
Free Store The free store is one of the two dynamic memory
areas, allocated/freed by new/delete. Object
lifetime can be less than the time the storage
is allocated; that is, free store objects can
have memory allocated without being immediately
initialized, and can be destroyed without the
memory being immediately deallocated. During
the period when the storage is allocated but
outside the object's lifetime, the storage may
be accessed and manipulated through a void* but
none of the proto-object's nonstatic members or
member functions may be accessed, have their
addresses taken, or be otherwise manipulated.
Heap The heap is the other dynamic memory area,
allocated/freed by malloc/free and their
variants. Note that while the default global
new and delete might be implemented in terms of
malloc and free by a particular compiler, the
heap is not the same as free store and memory
allocated in one area cannot be safely
deallocated in the other. Memory allocated from
the heap can be used for objects of class type
by placement-new construction and explicit
destruction. If so used, the notes about free
store object lifetime apply similarly here.
Global/Static Global or static variables and objects have
their storage allocated at program startup, but
may not be initialized until after the program
has begun executing. For instance, a static
variable in a function is initialized only the
first time program execution passes through its
definition. The order of initialization of
global variables across translation units is not
defined, and special care is needed to manage
dependencies between global objects (including
class statics). As always, uninitialized proto-
objects' storage may be accessed and manipulated
through a void* but no nonstatic members or
member functions may be used or referenced
outside the object's actual lifetime.
So finden Sie es selbst mit objdump -Sr
Um wirklich zu verstehen, was vor sich geht, müssen Sie die Linker-Verschiebung verstehen. Wenn Sie das noch nie berührt haben, lesen Sie zuerst diesen Beitrag.
Lassen Sie uns ein Linux x86-64 ELF-Beispiel analysieren, um es selbst zu sehen:
#include <stdio.h>
int f() {
static int i = 1;
i++;
return i;
}
int main() {
printf("%d\n", f());
printf("%d\n", f());
return 0;
}
Kompilieren mit:
gcc -ggdb -c main.c
Code dekompilieren mit:
objdump -Sr main.o
-S dekompiliert den Code mit der ursprünglichen Quelle vermischt
-r zeigt Umzugsinformationen
Innerhalb der Dekompilierung von f wir sehen:
static int i = 1;
i++;
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
6: R_X86_64_PC32 .data-0x4
und das .data-0x4 sagt, dass es zum ersten Byte des gehen wird .data Segment.
Die -0x4 ist da, weil wir relative RIP-Adressierung verwenden, also die %rip in der Anleitung u R_X86_64_PC32.
Es ist erforderlich, weil RIP auf die zeigt folgende Anweisung, die 4 Bytes danach beginnt 00 00 00 00 was umgesiedelt wird. Ich habe dies näher erklärt unter: https://stackoverflow.com/a/30515926/895245
Wenn wir dann die Quelle ändern, um i = 1 und dieselbe Analyse durchführen, kommen wir zu folgendem Schluss:
static int i = 0 geht weiter .bss
static int i = 1 geht weiter .data
Ich glaube nicht, dass es zu einer Kollision kommt. Die Verwendung von static auf Dateiebene (außerhalb von Funktionen) markiert die Variable als lokal für die aktuelle Kompilierungseinheit (Datei). Es ist nie außerhalb der aktuellen Datei sichtbar, muss also nie einen Namen haben, der extern verwendet werden kann.
Statik verwenden Innerhalb eine Funktion ist anders – die Variable ist nur für die Funktion sichtbar (ob statisch oder nicht), es ist nur ihr Wert, der über Aufrufe dieser Funktion hinweg beibehalten wird.
Tatsächlich macht Static zwei verschiedene Dinge, je nachdem, wo es sich befindet. Im beide In einigen Fällen ist die Sichtbarkeit der Variablen jedoch so eingeschränkt, dass Sie Namensraumkollisionen beim Verlinken leicht verhindern können.
Allerdings glaube ich, dass es in der gespeichert werden würde DATA Abschnitt, der dazu neigt, Variablen zu haben, die auf andere Werte als Null initialisiert werden. Dies ist natürlich ein Implementierungsdetail, nichts, was der Standard vorschreibt – es kümmert sich nur darum Verhalten, nicht wie die Dinge unter der Decke gemacht werden.
@paxdiablo: Sie haben zwei Arten von statischen Variablen erwähnt. Wer von ihnen macht diesen Artikel (en.wikipedia.org/wiki/Data_segment ) beziehen auf? Das Datensegment enthält auch die globalen Variablen (die von Natur aus genau das Gegenteil von statischen sind). 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)?
– Laser
18. April 2010 um 13:13 Uhr
@eSKay, es hat mit Sichtbarkeit zu tun. In einem Segment können Dinge gespeichert werden, die lokal für eine Kompilationseinheit sind, andere, die vollständig zugänglich sind. Ein Beispiel: Denken Sie an jede Comp-Einheit, die einen Block zum DATA-Segment beiträgt. Es weiß wo alles ist in diesem Block. Es veröffentlicht auch die Adressen jener Dinge in dem Block, zu denen es wünscht, dass andere Comp-Einheiten Zugriff darauf haben. Der Linker kann diese Adressen zur Linkzeit auflösen.
– paxdiablo
18. April 2010 um 14:29 Uhr
9970700cookie-checkWo werden statische Variablen in C und C++ gespeichert?yes
Die meisten Leute sagen Ihnen, dass sie im Abschnitt .DATA gespeichert werden sollten, anstatt Ihre Frage zu beantworten: Wo genau im Abschnitt .DATA und wie finden Sie wo. Ich sehe, Sie haben bereits eine Antwort markiert, also wissen Sie bereits, wie Sie sie finden?
– Lukmac
20. März 2011 um 20:50 Uhr
warum initialisiert und nicht initialisiert in verschiedenen Abschnitten platziert werden: linuxjournal.com/article/1059
– mhk
26. Juli 2012 um 16:39 Uhr
Der Speicherplatz, der Ihren globalen/statischen Variablen zur Laufzeit zugewiesen wird, hat nichts mit ihrer Namensauflösung zu tun, die während der Build-/Link-Zeit erfolgt. Nachdem die ausführbare Datei erstellt wurde, gibt es keine Namen mehr.
– Valdo
13. Oktober 2014 um 17:50 Uhr
Diese Frage ist bedeutungslos, da sie auf der falschen Prämisse basiert, dass “Namenskollision” von nicht exportierten Symbolen eine Sache sein kann, die existieren kann. Die Tatsache, dass es keine legitime Frage gibt, könnte erklären, wie schlimm einige der Antworten sind. Es ist schwer zu glauben, dass so wenige Leute das verstanden haben.
– Unterstrich_d
24. Juli 2016 um 22:26 Uhr