Wo im Speicher sind meine Variablen in C gespeichert?


155

Unter Berücksichtigung der Tatsache, dass der Speicher in vier Segmente unterteilt ist: Daten, Heap, Stack und Code, wo globale Variablen, statische Variablen, konstante Datentypen, lokale Variablen (in Funktionen definiert und deklariert), Variablen (in Hauptfunktion), Zeiger und dynamisch zugewiesener Speicherplatz (mit malloc und calloc) im Speicher gespeichert werden?

Ich denke, sie würden wie folgt vergeben:

  • Globale Variablen -------> Daten
  • Statische Variablen -------> Daten
  • Konstante Datentypen -----> Code
  • Lokale Variablen (deklariert und in Funktionen definiert) --------> Stapel
  • In der Hauptfunktion deklarierte und definierte Variablen -----> Heap
  • Zeiger (zum Beispiel char *arr, int *arr) -------> heap
  • Dynamisch zugewiesener Speicherplatz (mit malloc und calloc) --------> Stapel

Ich beziehe mich nur aus der C-Perspektive auf diese Variablen.

Bitte korrigieren Sie mich, wenn ich falsch liege, da ich neu bei C bin.


4
Typen werden nicht gespeichert.

5
mainist nur eine andere Funktion. Variablen werden auf den Stapel gelegt, es sei denn malloc, sie wären genau wie anderswo.
Simonc

4
Zeiger werden (normalerweise) auf dem Stapel gespeichert. Der Speicher, auf den sie verweisen (normalerweise über malloc / calloc zuweisen), befindet sich (normalerweise) auf dem Heap.
Jpm

3
dynamisch zugewiesener Platz (mit malloc, calloc) --------> heap
One Man Crew

3
Variablen deklariert und definiert in Hauptfunktion -----> Stack
One Man Crew

Antworten:


215

Sie haben einige davon richtig verstanden, aber wer auch immer die Fragen geschrieben hat, hat Sie in mindestens einer Frage ausgetrickst:

  • globale Variablen -------> Daten (korrekt)
  • statische Variablen -------> Daten (korrekt)
  • konstante Datentypen -----> Code und / oder Daten. Betrachten Sie Zeichenfolgenliterale für eine Situation, in der eine Konstante selbst im Datensegment gespeichert und Verweise darauf in den Code eingebettet werden
  • lokale Variablen (deklariert und in Funktionen definiert) --------> stack (korrekt)
  • Variablen deklariert und definiert in mainFunktion -----> Heap also Stack (der Lehrer hat versucht, Sie auszutricksen)
  • Zeiger (ex: char *arr, int *arr) -------> Heap - Daten oder Stapel, je nach Kontext. Mit C können Sie einen globalen staticZeiger oder einen Zeiger deklarieren. In diesem Fall würde der Zeiger selbst im Datensegment landen.
  • dynamisch zugewiesenen Raum (unter Verwendung von malloc, calloc, realloc) --------> Stapel heap

Es ist erwähnenswert, dass "Stapel" offiziell als "automatische Speicherklasse" bezeichnet wird.


6
Erwähnenswert ist auch, dass der Haufen offiziell überhaupt nichts genannt wird. Der zugewiesene Speicher kommt von irgendwoher, es gibt keinen Namen im Standard für dieses "irgendwo".
Steve Jessop

6
Auf einigen Systemen (nämlich Linux und * BSD) gibt es auch Systeme, allocadie ähnlich funktionieren malloc, aber die Stapelzuweisung durchführen.
Andreas Grapentin

Wohin geht die in einer Methode deklarierte const-Variable?
Mahori

@ Ravi Die gleiche Stelle, an der sich die restlichen Konstanten befinden (Punkt 3 oben).
Dasblinkenlight

Ich verwende GCC 4.8.1 und es scheint keine const-Variable local to main im DATA-Segment zu speichern. Unten finden Sie Code und Speicherzuordnung für 3 solcher Programme: Code 1: int main (void) {// char a [10] = "HELLO"; // 1 // const char a [10] = "HALLO"; // 2 return 0; } SPEICHERKARTE FÜR OBEN: Textdaten bss dec hex Dateiname 7264 1688 1040 9992 2708 a.exe SPEICHERKARTE FÜR 2: Textdaten bss dec hex Dateiname 7280 1688 1040 10008 2718 a.exe SPEICHERKARTE FÜR 3: Textdaten bss dec hex Dateiname 7280 1688 1040 10008 2718 a.exe
Mahori

123

Für zukünftige Besucher, die sich für diese Speichersegmente interessieren, schreibe ich wichtige Punkte zu 5 Speichersegmenten in C:

Einige Köpfe hoch:

  1. Immer wenn ein C-Programm ausgeführt wird, wird im RAM etwas Speicher für die Programmausführung zugewiesen. Dieser Speicher wird zum Speichern des häufig ausgeführten Codes (Binärdaten), Programmvariablen usw. verwendet. Die folgenden Speichersegmente sprechen über dasselbe:
  2. Typischerweise gibt es drei Arten von Variablen:
    • Lokale Variablen (in C auch als automatische Variablen bezeichnet)
    • Globale Variablen
    • Statische Variablen
    • Sie können globale statische oder lokale statische Variablen haben, aber die obigen drei sind die übergeordneten Typen.

5 Speichersegmente in C:

1. Codesegment

  • Das Codesegment, auch als Textsegment bezeichnet, ist der Speicherbereich, der den häufig ausgeführten Code enthält.
  • Das Codesegment ist häufig schreibgeschützt, um das Risiko zu vermeiden, durch Programmierfehler wie Pufferüberlauf usw. überschrieben zu werden.
  • Das Codesegment enthält keine Programmvariablen wie lokale Variablen ( in C auch als automatische Variablen bezeichnet ), globale Variablen usw.
  • Basierend auf der C-Implementierung kann das Codesegment auch schreibgeschützte Zeichenfolgenliterale enthalten. Wenn Sie dies printf("Hello, world")dann tun , wird im Code- / Textsegment die Zeichenfolge "Hallo Welt" erstellt. Sie können dies mit dem sizeBefehl unter Linux überprüfen .
  • Weiterführende Literatur

Datensegment

Das Datensegment ist in die folgenden zwei Teile unterteilt und liegt typischerweise unterhalb des Heap-Bereichs oder in einigen Implementierungen oberhalb des Stapels, aber das Datensegment liegt niemals zwischen dem Heap- und dem Stapelbereich.

2. Nicht initialisiertes Datensegment

  • Dieses Segment wird auch als bss bezeichnet .
  • Dies ist der Teil des Speichers, der Folgendes enthält:
    1. Nicht initialisierte globale Variablen (einschließlich Zeigervariablen)
    2. Nicht initialisierte konstante globale Variablen .
    3. Nicht initialisierte lokale statische Variablen .
  • Jede globale oder statische lokale Variable, die nicht initialisiert ist, wird im nicht initialisierten Datensegment gespeichert
  • Beispiel: Globale Variable int globalVar;oder statische lokale Variable static int localStatic;werden im nicht initialisierten Datensegment gespeichert.
  • Wenn Sie eine globale Variable deklarieren und als 0oder NULLdann noch initialisieren, wird sie in ein nicht initialisiertes Datensegment oder bss verschoben.
  • Weiterführende Literatur

3. Initialisiertes Datensegment

  • Dieses Segment speichert:
    1. Initialisierte globale Variablen (einschließlich Zeigervariablen)
    2. Initialisierte konstante globale Variablen .
    3. Initialisierte lokale statische Variablen .
  • Beispiel: Globale Variable int globalVar = 1;oder statische lokale Variable static int localStatic = 1;werden im initialisierten Datensegment gespeichert.
  • Dieses Segment kann weiter in einen initialisierten schreibgeschützten Bereich und einen initialisierten Lese- / Schreibbereich unterteilt werden . Initialisierte konstante globale Variablen werden in den initialisierten schreibgeschützten Bereich verschoben, während Variablen, deren Werte zur Laufzeit geändert werden können, in den initialisierten Lese- / Schreibbereich verschoben werden .
  • Die Größe dieses Segments wird durch die Größe der Werte im Quellcode des Programms bestimmt und ändert sich zur Laufzeit nicht .
  • Weiterführende Literatur

4. Stapelsegment

  • Das Stapelsegment wird verwendet, um Variablen zu speichern, die innerhalb von Funktionen erstellt werden ( Funktion kann Hauptfunktion oder benutzerdefinierte Funktion sein ), wie Variablen
    1. Lokale Variablen der Funktion (einschließlich Zeigervariablen)
    2. An die Funktion übergebene Argumente
    3. Absender
  • Im Stapel gespeicherte Variablen werden entfernt, sobald die Funktionsausführung abgeschlossen ist.
  • Weiterführende Literatur

5. Heap-Segment

  • Dieses Segment soll die dynamische Speicherzuordnung unterstützen. Wenn die Programmierer etwas Speicher dynamisch dann in C zuordnen wollen , ist es fertig mit den malloc, callocoder reallocMethoden.
  • Wenn int* prt = malloc(sizeof(int) * 2)dann beispielsweise acht Bytes im Heap zugewiesen werden und die Speicheradresse dieses Speicherorts zurückgegeben und in einer ptrVariablen gespeichert wird. Die ptrVariable befindet sich je nach Deklaration / Verwendung entweder im Stapel oder im Datensegment.
  • Weiterführende Literatur

Sollte dies nicht initialisiert werden, anstatt in 3. Initialisiertes Datensegment nicht initialisiert zu werden?
Suraj Jain

Betreff "im nicht initialisierten Datensegment gespeichert" (mehrere Instanzen): Meinen Sie "im nicht initialisierten Datensegment gespeichert" ?
Peter Mortensen

@ PeterMortensen Ich meine beide Dinge. "Jede globale oder statische lokale Variable, die nicht initialisiert ist, wird im nicht initialisierten Datensegment gespeichert"
Hagrawal

Wie können wir eine globale statische Variable in C haben?

Unter "einige Heads-Ups" fand ich diesen Punkt "Sie können globale statische oder lokale statische Variablen haben, aber die obigen drei sind die übergeordneten Typen." in dem du dich auf den Begriff "global static" beziehst. Mein Punkt ist, dass eine statische Variable nicht global sein kann. Wenn eine Variable global sein muss, sollte sie zugänglich sein, bis die Ausführung des Programms abgeschlossen ist. Bitte erklären Sie und helfen Sie, wenn ich falsch liege.

11

Korrigierte deine falschen Sätze

constant data types ----->  code //wrong

lokale konstante Variablen -----> Stapel

initialisierte globale konstante Variable -----> Datensegment

nicht initialisierte globale konstante Variable -----> bss

variables declared and defined in main function  ----->  heap //wrong

Variablen, die in der Hauptfunktion -----> stack deklariert und definiert sind

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

Zeiger (z. B. char * arr, int * arr) -------> Die Größe dieser Zeigervariablen befindet sich im Stapel.

Bedenken Sie, dass Sie den Speicher von n Bytes (mit mallocoder calloc) dynamisch zuweisen und dann eine Zeigervariable erstellen, um darauf zu zeigen. Jetzt, da sich nSpeicherbytes im Heap befinden und die Zeigervariable 4 Bytes (wenn 64-Bit-Maschine 8 Bytes) benötigt, werden diese im Stapel gespeichert, um den Startzeiger der nBytes des Speicherblocks zu speichern .

Hinweis: Zeigervariablen können auf den Speicher eines beliebigen Segments zeigen.

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

dynamisch zugewiesener Speicherplatz (mit malloc, calloc) --------> Heap


Zeiger können entweder im Stapel oder im Heap sein (siehe insbesondere: Zeiger auf Zeiger)
Argentage

@airza: Jetzt aktualisiert. Eigentlich habe ich nur diese Details aktualisiert :)
Rashok

Können Sie in der folgenden Speicherzuordnung bitte darauf hinweisen, wo sich Stapel und Heap befinden? Ich bin nicht sicher, ob dies die richtige Frage ist, da Stapel und Speicher möglicherweise nur zur Laufzeit verfügbar sind. SPEICHERKARTE: "Textdaten bss dec hex Dateiname 7280 1688 1040 10008 2718 a.exe"
Mahori

7

Eine beliebte Desktop-Architektur unterteilt den virtuellen Speicher eines Prozesses in mehrere Segmente :

  • Textsegment: Enthält den ausführbaren Code. Der Befehlszeiger nimmt Werte in diesem Bereich an.

  • Datensegment: Enthält globale Variablen (dh Objekte mit statischer Verknüpfung). Unterteilt in schreibgeschützte Daten (z. B. Zeichenfolgenkonstanten) und nicht initialisierte Daten ("BSS").

  • Stapelsegment: Enthält den dynamischen Speicher für das Programm, dh den freien Speicher ("Heap") und die lokalen Stapelrahmen für alle Threads. Traditionell wuchsen der C-Stapel und der C-Haufen von entgegengesetzten Enden in das Stapelsegment hinein, aber ich glaube, dass die Praxis aufgegeben wurde, weil sie zu unsicher ist.

Das AC-Programm fügt normalerweise Objekte mit statischer Speicherdauer in das Datensegment, dynamisch zugewiesene Objekte im freien Speicher und automatische Objekte im Aufrufstapel des Threads ein, in dem es sich befindet.

Auf anderen Plattformen, wie dem alten x86-Real-Modus oder auf eingebetteten Geräten, können die Dinge offensichtlich radikal anders sein.


"Ich glaube, dass die Praxis aufgegeben wurde, weil sie zu unsicher ist" - und es unmöglich macht, Threads zu implementieren, da Sie seitdem mehr als einen Stapel pro Programm benötigen und sie nicht alle am Ende sein können :-)
Steve Jessop

@SteveJessop: Ja, das habe ich auch gedacht. Aber Fäden gibt es schon lange - ich weiß nicht, ob alle Fadenstapel auch rückwärts gewachsen sind oder ob sie wie der Haufen wachsen würden ... heutzutage geht sowieso alles in die gleiche Richtung und es gibt Wachen Seiten.
Kerrek SB

6

Ich beziehe mich nur aus der C-Perspektive auf diese Variablen.

Aus Sicht der C-Sprache kommt es nur auf Umfang, Umfang, Verknüpfung und Zugang an. Wie genau Elemente verschiedenen Speichersegmenten zugeordnet werden, hängt von der jeweiligen Implementierung ab, und das ist unterschiedlich. Der Sprachstandard spricht nicht über Speichersegmente überhaupt . Die meisten modernen Architekturen verhalten sich meist genauso. Blockbereichsvariablen und Funktionsargumente werden vom Stapel zugewiesen, Dateibereichsvariablen und statische Variablen werden von einem Daten- oder Codesegment zugewiesen, dynamischer Speicher wird von einem Heap zugewiesen, einige konstante Daten werden in schreibgeschützten Segmenten gespeichert , etc.


3

Eine Sache, die man bei der Speicherung beachten muss, ist die Als-ob- Regel . Der Compiler muss eine Variable nicht an einer bestimmten Stelle ablegen, sondern kann sie an einer beliebigen Stelle platzieren, solange sich das kompilierte Programm so verhält, als würde es in der abstrakten C-Maschine gemäß den Regeln der abstrakten C-Maschine ausgeführt. Dies gilt für alle Speicherdauer . Beispielsweise:

  • Eine Variable, auf die nicht alle zugegriffen wird, kann vollständig entfernt werden - sie hat keinen Speicher ... überall. Beispiel - Sehen Sie, wie es 42im generierten Assembly-Code ist, aber keine Anzeichen von 404.
  • Eine Variable mit automatischer Speicherdauer, deren Adresse nicht vergeben wurde, muss überhaupt nicht im Speicher gespeichert werden. Ein Beispiel wäre eine Schleifenvariable.
  • Eine Variable, die sich im Speicher befindet constoder effektiv constnicht befinden muss. Beispiel - Der Compiler kann dies fooeffektiv beweisen constund seine Verwendung in den Code einbinden. barhat eine externe Verknüpfung und der Compiler kann nicht beweisen, dass er außerhalb des aktuellen Moduls nicht geändert wird, daher ist er nicht inline.
  • Ein Objekt, das mit zugewiesen wurde, mallocmuss sich nicht im Speicher befinden, der vom Heap zugewiesen wurde! Beispiel - Beachten Sie, dass der Code keinen Aufruf hat mallocund der Wert 42 auch nie im Speicher gespeichert ist. Er wird in einem Register gespeichert!
  • Somit geht ein Objekt, das von zugewiesen wurde mallocund dessen Referenz verloren geht, ohne die Zuordnung des Objekts aufzuheben, ohne free dass Speicher verloren gehen muss ...
  • Das von zugewiesene Objekt mallocmuss sich untersbrk(0) Unixen nicht innerhalb des Heaps unterhalb des Programms break ( ) befinden ...

1

Zeiger (Beispiel: char * arr, int * arr) -------> Heap

Nein, sie können sich auf dem Stapel oder im Datensegment befinden. Sie können überall zeigen.


Die Aussagen über mainund dynamisch zugewiesenen Variablen sind ebenfalls falsch
simonc

Nicht nur auf dem Stapel oder Datensegment. Stellen Sie sich einen Zeiger vor, der auf ein Array von Zeigern zeigt. In diesem Fall werden die Zeiger im Array auf dem Heap gespeichert.
Sebi2020

1
  • Variablen / automatische Variablen ---> Stapelabschnitt
  • Dynamisch zugewiesene Variablen ---> Heap-Abschnitt
  • Initialisierte globale Variablen -> Datenabschnitt
  • Nicht initialisierte globale Variablen -> Datenabschnitt (bss)
  • Statische Variablen -> Datenabschnitt
  • String-Konstanten -> Textabschnitt / Codeabschnitt
  • Funktionen -> Textabschnitt / Codeabschnitt
  • Textcode -> Textabschnitt / Codeabschnitt
  • Register -> CPU-Register
  • Befehlszeileneingaben -> Umgebungs- / Befehlszeilenabschnitt
  • Umgebungsvariablen -> Umgebungsvariablen / Befehlszeilenabschnitt

Was ist ein Umwelt- / Befehlszeilenabschnitt? Existieren sie unter Linux?
Haoyuan Ge

-1

Minimal ausführbare Linux-Beispiele mit Disassemblierungsanalyse

Da dies ein Implementierungsdetail ist, das nicht durch Standards spezifiziert ist, schauen wir uns nur an, was der Compiler bei einer bestimmten Implementierung tut.

In dieser Antwort werde ich entweder auf bestimmte Antworten verweisen, die die Analyse durchführen, oder die Analyse direkt hier bereitstellen und alle Ergebnisse hier zusammenfassen.

Alle diese Versionen befinden sich in verschiedenen Ubuntu / GCC-Versionen, und die Ergebnisse sind wahrscheinlich über die Versionen hinweg ziemlich stabil. Wenn wir jedoch Variationen finden, geben wir genauere Versionen an.

Lokale Variable innerhalb einer Funktion

Sei es mainoder eine andere Funktion:

void f(void) {
    int my_local_var;
}

Wie gezeigt unter: Was bedeutet <Wert optimiert aus> in GDB?

  • -O0: Stapel
  • -O3: registriert, wenn sie nicht verschüttet werden, sonst stapeln

Die Motivation, warum der Stapel vorhanden ist, finden Sie unter: Welche Funktion haben die Push / Pop-Anweisungen, die für Register in der x86-Assembly verwendet werden?

Globale Variablen und staticFunktionsvariablen

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}

char * und char c[]

Wie unter: Wo werden statische Variablen in C und C ++ gespeichert?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

TODO werden auch sehr große String-Literale auf den Stapel gelegt? Oder .data? Oder schlägt die Kompilierung fehl?

Funktionsargumente

void f(int i, int j);

Muss die entsprechende Aufrufkonvention durchlaufen, z. B.: Https://en.wikipedia.org/wiki/X86_calling_conventions for X86, in der entweder bestimmte Register oder Stapelpositionen für jede Variable angegeben sind.

Dann, wie unter Was bedeutet <Wert optimiert aus> in gdb? , -O0Dann schlürft alles in den Stapel, während -O3versucht Register , so viel wie möglich zu nutzen.

Wenn die Funktion jedoch inline wird, werden sie wie normale Einheimische behandelt.

const

Ich glaube, dass es keinen Unterschied macht, weil Sie es wegschreiben können.

Wenn der Compiler hingegen feststellen kann, dass einige Daten niemals beschrieben werden, kann er sie theoretisch einfügen .rodata auch dann platzieren, wenn nicht const.

TODO-Analyse.

Zeiger

Sie sind Variablen (die Adressen enthalten, die Zahlen sind), genau wie alle anderen :-)

malloc

Die Frage macht wenig Sinn für malloc, da malloces sich um eine Funktion handelt, und in:

int *i = malloc(sizeof(int));

*i ist eine Variable, die eine Adresse enthält, daher fällt sie auf den obigen Fall.

Wie Malloc intern funktioniert, wenn Sie es aufrufen, markiert der Linux-Kernel bestimmte Adressen als beschreibbar in seinen internen Datenstrukturen, und wenn sie anfänglich vom Programm berührt werden, tritt ein Fehler auf und der Kernel aktiviert die Seitentabellen, die den Zugriff ermöglichen passieren ohne segfaul: Wie funktioniert x86-Paging?

Beachten Sie jedoch, dass dies im Grunde genau das ist, was der execSystemaufruf unter der Haube tut, wenn Sie versuchen, eine ausführbare Datei auszuführen: Er markiert Seiten, auf die geladen werden soll, und schreibt das Programm dort. Siehe auch: Wie bringt der Kernel eine ausführbare Binärdatei zum Laufen? Linux? Abgesehen davon execgibt es einige zusätzliche Einschränkungen hinsichtlich des Ladeorts (z. B. ist der Code nicht verschiebbar ).

Der genaue verwendete Systemaufruf mallocist mmapin modernen 2020-Implementierungen enthalten und wurde in der Vergangenheit brkverwendet: Verwendet malloc () brk () oder mmap ()?

Dynamische Bibliotheken

Grundsätzlich mmapin den Speicher gelangen lassen: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

Umweltvariablen und main'sargv

Über dem ersten Stapel: /unix/75939/where-is-the-environment-string-actual-stored TODO Warum nicht in .data?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.