it-swarm.com.de

Speicherzuordnung / Freigabe?

In letzter Zeit habe ich mir die Speicherzuordnung angesehen und bin etwas verwirrt über die Grundlagen. Ich war nicht in der Lage, mich um die einfachen Dinge zu kümmern. Was bedeutet es, Speicher zuzuweisen? Was geschieht? Ich würde mich über Antworten auf folgende Fragen freuen:

  1. Wo ist der "Speicher", der zugewiesen wird?
  2. Was ist das "Gedächtnis"? Platz in einem Array? Oder etwas anderes?
  3. Was passiert genau, wenn dieser "Speicher" zugewiesen wird?
  4. Was passiert genau, wenn der Speicher freigegeben wird?
  5. Es würde mir auch sehr helfen, wenn jemand antworten könnte, was malloc in diesen C++ - Zeilen tut:

    char* x; 
    x = (char*) malloc (8);
    

Vielen Dank.

36
Isaac

Das Gedächtnismodell

Der C++ - Standard hat ein Speichermodell . Es wird versucht, den Speicher in einem Computersystem auf generische Weise zu modellieren. Der Standard definiert, dass ein Byte eine Speichereinheit im Speichermodell ist und dass der Speicher aus Bytes besteht (§1.7):

Die grundlegende Speichereinheit im C++ - Speichermodell ist das Byte. [...] Der für ein C++ - Programm verfügbare Speicher besteht aus einer oder mehreren Folgen zusammenhängender Bytes.

Das Objektmodell

Der Standard liefert immer ein Objektmodell . Dies gibt an, dass ein Objekt eine Speicherregion ist (also aus Bytes besteht und sich im Speicher befindet) (§1.8):

Die Konstrukte in einem C++ - Programm erstellen, zerstören, referenzieren, greifen auf Objekte zu und bearbeiten diese. Ein Objekt ist ein Speicherbereich.

Also los geht's. Speicher ist, wo Objekte gespeichert werden. Um ein Objekt im Speicher zu speichern, muss der erforderliche Speicherbereich zugewiesen werden.

Zuweisungs- und Freigabefunktionen

Der Standard bietet zwei implizit deklarierte globale Bereichszuweisungsfunktionen:

void* operator new(std::size_t);
void* operator new[](std::size_t);

Wie diese implementiert werden, ist nicht das Anliegen des Standards. Alles, was zählt, ist, dass sie einen Zeiger auf einen Speicherbereich mit der Anzahl von Bytes zurückgeben, die dem übergebenen Argument entspricht (§3.7.4.1):

Die Zuweisungsfunktion versucht, die angeforderte Speichermenge zuzuweisen. Wenn es erfolgreich ist, gibt es die Adresse des Anfangs eines Speicherblocks zurück, dessen Länge in Bytes mindestens so groß sein muss wie die angeforderte Größe. Es gibt keine Einschränkungen für den Inhalt des zugewiesenen Speichers bei Rückkehr von der Zuweisungsfunktion.

Es definiert auch zwei entsprechende Freigabefunktionen:

void operator delete(void*);
void operator delete[](void*);

Welche definiert sind, um die Zuordnung des zuvor zugewiesenen Speichers aufzuheben (§3.7.4.2):

Wenn das Argument für eine Aufhebungsfunktion in der Standardbibliothek ein Zeiger ist, der nicht der Nullzeigerwert ist (4.10), gibt die Aufhebungsfunktion die Zuordnung des Speichers auf, auf den der Zeiger verweist, und macht alle Zeiger ungültig, die auf einen Teil des freigegebenen Speichers verweisen .

new und delete

In der Regel sollten Sie die Zuweisungs- und Freigabefunktionen nicht direkt verwenden müssen, da sie nur nicht initialisierten Speicher bereitstellen. Stattdessen sollten Sie in C++ new und delete verwenden, um Objekte dynamisch zuzuweisen. Ein neuer Ausdruck erhält Speicher für den angeforderten Typ, indem er eine der obigen Zuordnungsfunktionen verwendet und dieses Objekt dann auf irgendeine Weise initialisiert. Zum Beispiel reserviert new int() Platz für ein int Objekt und initialisiert es dann auf 0. Siehe §5.3.4:

Ein neuer Ausdruck erhält Speicherplatz für das Objekt durch Aufrufen einer Zuordnungsfunktion (3.7.4.1).

[...]

Ein neuer Ausdruck , der ein Objekt vom Typ T erzeugt, initialisiert dieses Objekt [...]

In der entgegengesetzten Richtung ruft delete den Destruktor eines Objekts (falls vorhanden) auf und gibt die Zuordnung des Speichers auf (§5.3.5):

Wenn der Wert des Operanden des Löschausdrucks kein Nullzeigerwert ist, ruft der Löschausdruck den Destruktor auf (falls vorhanden) für das Objekt oder die Elemente des zu löschenden Arrays.

[...]

Wenn der Wert des Operanden des Löschausdrucks kein Nullzeigerwert ist, ruft der Löschausdruck eine Freigabe auf Funktion (3.7.4.2).

Andere Zuordnungen

Dies sind jedoch nicht die einzigen Methoden, mit denen Speicher zugewiesen oder freigegeben wird. Viele Konstrukte der Sprache erfordern implizit die Zuweisung von Speicherplatz. Wenn Sie beispielsweise eine Objektdefinition wie int a; Angeben, ist auch Speicher erforderlich (§7):

Eine Definition bewirkt, dass die entsprechende Speichermenge reserviert und eine entsprechende Initialisierung (8.5) durchgeführt wird.

C-Standardbibliothek: malloc und free

Darüber hinaus enthält der Header <cstdlib> Den Inhalt der C-Standardbibliothek stdlib.h, Die die Funktionen malloc und free enthält. Sie sind auch durch den C-Standard definiert, um Speicher zuzuweisen und die Zuordnung aufzuheben, ähnlich wie die Zuweisungs- und Freigabefunktionen, die durch den C++ - Standard definiert sind. Hier ist die Definition von malloc (C99 §7.20.3.3):

void *malloc(size_t size);
Beschreibung
Die Funktion malloc reserviert Platz für ein Objekt, dessen Größe durch size angegeben wird und dessen Wert unbestimmt ist.
Rückgabe
Die Funktion malloc gibt entweder einen Nullzeiger oder einen Zeiger auf den zugewiesenen Speicherplatz zurück.

Und die Definition von free (C99 §7.20.3.2):

void free(void *ptr);
Beschreibung
Die Funktion free bewirkt, dass der von ptr angegebene Speicherplatz freigegeben wird, dh für die weitere Zuordnung verfügbar gemacht wird. Wenn ptr ein Nullzeiger ist, wird keine Aktion ausgeführt. Anderenfalls, wenn das Argument nicht mit einem Zeiger übereinstimmt, der zuvor von der Funktion calloc, malloc oder realloc zurückgegeben wurde, oder wenn die Zuordnung des Speicherplatzes durch einen Aufruf aufgehoben wurde Bei free oder realloc ist das Verhalten undefiniert.

Es gibt jedoch nie eine gute Ausrede, malloc und free in C++ zu verwenden. Wie zuvor beschrieben, hat C++ seine eigenen Alternativen.


Antworten auf Fragen

So beantworten Sie Ihre Fragen direkt:

  1. Wo ist der "Speicher", der zugewiesen wird?

    Dem C++ Standard ist das egal. Es wird einfach gesagt, dass das Programm einen Speicher hat, der aus Bytes besteht. Dieser Speicher kann zugewiesen werden.

  2. Was ist das "Gedächtnis"? Platz in einem Array? Oder etwas anderes?

    Für den Standard ist der Speicher nur eine Folge von Bytes. Dies ist absichtlich sehr allgemein, da der Standard nur versucht typische Computersysteme zu modellieren . Sie können es sich größtenteils als ein Modell des RAM Ihres Computers vorstellen.

  3. Was passiert genau, wenn dieser "Speicher" zugewiesen wird?

    Durch das Zuweisen von Speicher wird ein bestimmter Speicherbereich für das Programm verfügbar. Objekte werden im zugewiesenen Speicher initialisiert. Sie müssen lediglich wissen, dass Sie Speicher zuweisen können. Die tatsächliche Zuordnung des physischen Speichers zu Ihrem Prozess wird in der Regel vom Betriebssystem vorgenommen.

  4. Was passiert genau, wenn der Speicher freigegeben wird?

    Das Freigeben eines zuvor zugewiesenen Speichers führt dazu, dass dieser Speicher für das Programm nicht verfügbar ist. Es wird Speicher freigegeben.

  5. Es würde mir auch sehr helfen, wenn jemand antworten könnte, was malloc in diesen C++ - Zeilen tut:

    char* x; 
    x = (char*) malloc (8);
    

    Hier reserviert malloc einfach 8 Bytes Speicher. Der zurückgegebene Zeiger wird in einen char* Umgewandelt und in x gespeichert.

60

1) Wo ist der "Speicher", der zugewiesen wird?

Dies ist abhängig von Ihrem Betriebssystem, der Programmierumgebung (gcc vs Visual C++ vs Borland C++ vs irgendetwas anderem), dem Computer, dem verfügbaren Speicher usw. völlig anders. Im Allgemeinen wird der Speicher aus dem so genannten Heap, der Region des gerade wartenden Speichers, zugewiesen um für Sie zu verwenden. In der Regel wird der verfügbare Arbeitsspeicher verwendet. Es gibt aber immer Ausnahmen. Zum größten Teil ist es kein großes Problem, solange es uns Erinnerung gibt, woher es kommt. Es gibt spezielle Speichertypen, wie z. B. virtuellen Speicher, der sich möglicherweise zu einem bestimmten Zeitpunkt tatsächlich in RAM befindet oder nicht und auf Ihre Festplatte (oder ein ähnliches Speichergerät) verschoben wird, wenn Ihnen der tatsächliche Speicherplatz ausgeht Erinnerung. Eine vollständige Erklärung wäre sehr lang!

2) Was ist das "Gedächtnis"? Platz in einem Array? Oder was anderes?

Der Speicher ist im Allgemeinen das RAM in Ihrem Computer. Wenn es hilfreich ist, sich den Speicher als ein gigantisches "Array" vorzustellen, funktioniert er gewiss wie eines, und dann als eine Tonne Bytes (8-Bit-Werte, ähnlich wie unsigned char - Werte). Es beginnt bei einem Index von 0 am Ende des Speichers. Genau wie zuvor gibt es hier jedoch Unmengen von Ausnahmen und einige Teile des Speichers sind möglicherweise auf Hardware abgebildet oder existieren überhaupt nicht!

) Was passiert genau, wenn dieser "Speicher" zugewiesen wird?

Zu jedem Zeitpunkt sollte (wir hoffen wirklich!) Ein Teil davon für die Zuweisung durch Software verfügbar sein. Wie es zugewiesen wird, hängt stark vom System ab. Im Allgemeinen wird ein Speicherbereich zugewiesen, der Zuweiser markiert ihn als verwendet, und anschließend erhalten Sie einen Zeiger, der dem Programm mitteilt, wo sich im gesamten Speicher Ihres Systems dieser Speicher befindet. In Ihrem Beispiel findet das Programm einen aufeinanderfolgenden Block mit 8 Byte (Zeichen) und gibt einen Zeiger zurück, der auf den Block verweist, der als "in Verwendung" markiert wurde.

4) Was passiert genau, wenn der Speicher freigegeben wird?

Das System markiert diesen Speicher als wieder verfügbar. Dies ist unglaublich kompliziert, da dies häufig zu Speicherlücken führt. Ordnen Sie 8 Bytes zu, dann 8 weitere Bytes. Geben Sie dann die Zuordnung der ersten 8 Bytes auf, und Sie haben eine Lücke. Es gibt ganze Bücher über die Aufhebung der Zuweisung, die Speicherzuweisung usw. Hoffentlich ist die kurze Antwort ausreichend!

5) Es würde mir auch sehr helfen, wenn jemand antworten könnte, was malloc in diesen C++ - Zeilen macht:

WIRKLICH grob und unter der Annahme, dass es sich um eine Funktion handelt (im Übrigen, tun Sie dies niemals, da dies die Zuordnung Ihres Speichers nicht aufhebt und einen Speicherverlust verursacht):

void mysample() {
  char *x; // 1
  x = (char *) malloc(8); // 2
}

1) Dies ist ein Zeiger, der im lokalen Stapelspeicher reserviert ist. Es wurde nicht initialisiert, so dass es auf das verweist, was sich in diesem Speicher befand.

2) Es ruft malloc mit einem Parameter von 8 auf. Die Besetzung teilt C/C++ lediglich mit, dass Sie beabsichtigen, dass es ein (char *) ist, da es ein (void *) zurückgibt, was bedeutet, dass kein Typ angewendet wurde. Der resultierende Zeiger wird dann in Ihrer x-Variablen gespeichert.

In einer sehr groben x86 32-Bit-Assembly wird dies vage aussehen

PROC mysample:
  ; char *x;
  x = DWord Ptr [ebp - 4]
  enter 4, 0   ; Enter and preserve 4 bytes for use with 

  ; x = (char *) malloc(8);
  Push 8       ; We're using 8 for Malloc
  call malloc  ; Call malloc to do it's thing
  sub esp, 4   ; Correct the stack
  mov x, eax   ; Store the return value, which is in EAX, into x

  leave
  ret

Die tatsächliche Zuordnung ist in Punkt 3 vage beschrieben. Malloc ruft hierfür normalerweise nur eine Systemfunktion auf, die den Rest erledigt, und wie alles andere hier ist es von Betriebssystem zu Betriebssystem, von System zu System usw. völlig unterschiedlich.

11
Mark Ormston

1. Wo ist der "Speicher", der zugewiesen wird?

Aus sprachlicher Sicht ist dies nicht spezifiziert, und vor allem, weil die feinen Details oft keine Rolle spielen. Außerdem neigt der C++ - Standard dazu, fehlerhafte Hardwaredetails anzugeben, um unnötige Einschränkungen zu minimieren (sowohl auf den Plattformen, auf denen Compiler ausgeführt werden können, als auch auf möglichen Optimierungen).

die Antwort von sftrabbit gibt einen großartigen Überblick über dieses Ende der Dinge (und es ist alles, was Sie wirklich brauchen), aber ich kann ein paar erarbeitete Beispiele geben, falls das hilft.

Beispiel 1:

Auf einem ausreichend alten Einzelbenutzercomputer (oder einem ausreichend kleinen eingebetteten Computer) ist der größte Teil des physischen RAM möglicherweise direkt für Ihr Programm verfügbar. In diesem Szenario ist das Aufrufen von malloc oder new im Wesentlichen eine interne Buchhaltung, sodass die Laufzeitbibliothek nachverfolgen kann, welche Abschnitte dieses RAM derzeit verwendet werden. Sie können dies manuell tun, aber es wird ziemlich schnell langweilig.

Beispiel 2:

Auf einem modernen Multitasking-Betriebssystem wird das physische RAM mit vielen Prozessen und anderen Aufgaben, einschließlich Kernel-Threads, gemeinsam genutzt. Es wird auch für das Zwischenspeichern von Datenträgern und die E/A-Pufferung im Hintergrund verwendet und durch das Subsystem für virtuellen Speicher erweitert, das Daten auf Datenträger (oder ein anderes Speichergerät) auslagern kann, wenn sie nicht verwendet werden.

In diesem Szenario prüft der Aufruf von new möglicherweise zuerst, ob in Ihrem Prozess intern bereits genügend Speicherplatz frei ist, und fordert gegebenenfalls weitere Informationen vom Betriebssystem an. Der zurückgegebene Speicher kann physisch oder virtuell sein (in diesem Fall kann physisch RAM nicht zugewiesen werden, um ihn zu speichern, bis tatsächlich auf ihn zugegriffen wird). Zumindest ohne die Verwendung plattformspezifischer APIs können Sie den Unterschied nicht einmal feststellen, da sich die Speicherhardware und der Kernel zusammenschließen, um ihn vor Ihnen zu verbergen.

2. Was ist das "Gedächtnis"? Platz in einem Array? Oder etwas anderes?

In Beispiel 1 ist es so etwas wie Platz in einem Array: Die zurückgegebene Adresse kennzeichnet einen adressierbaren Teil des physischen RAM. Auch hier sind RAM Adressen nicht unbedingt flach oder zusammenhängend - einige Adressen sind möglicherweise für ROM- oder E/A-Ports reserviert.

In Beispiel 2 ist dies ein Index für etwas Virtuelleres: den Adressraum Ihres Prozesses. Dies ist eine Abstraktion, die verwendet wird, um die zugrunde liegenden Details des virtuellen Speichers vor Ihrem Prozess zu verbergen. Wenn Sie auf diese Adresse zugreifen, greift die Speicherhardware möglicherweise direkt auf einen realen RAM-Speicher zu, oder Sie müssen das Subsystem für den virtuellen Speicher auffordern, einen solchen bereitzustellen.

3 . Was passiert genau, wenn dieser "Speicher" zugewiesen wird?

Im Allgemeinen wird ein Zeiger zurückgegeben, mit dem Sie so viele Bytes speichern können, wie Sie angefordert haben. In beiden Fällen führt der Operator malloc oder new einen Housekeeping-Vorgang durch, um zu ermitteln, welche Teile des Adressraums Ihres Prozesses verwendet werden und welche frei sind.

4. Was passiert genau, wenn der Speicher freigegeben wird?

Auch hier führt free oder delete im Allgemeinen eine Haushaltspflege durch, damit sie wissen, dass Speicher verfügbar ist, der neu zugewiesen werden kann.

Es würde mir auch sehr helfen, wenn jemand antworten könnte, was malloc in diesen C++ - Zeilen tut:

char* x; 
x = (char*) malloc (8);

Es wird ein Zeiger zurückgegeben, der entweder NULL ist (wenn die gewünschten 8 Bytes nicht gefunden wurden) oder ein Nicht-NULL-Wert.

Zu diesem Nicht-NULL-Wert können Sie nur Folgendes sinnvoll sagen:

  • es ist legal (und sicher), auf jedes dieser 8 Bytes zuzugreifen x[0]..x[7],
  • es ist illegal (undefiniertes Verhalten), auf x[-1] oder x[8] zuzugreifen oder tatsächlich beliebigx[i], es sei denn, 0 <= i <= 7
  • es ist legal, vergleiche irgendeinen von x, x+1, ..., x+8 (obwohl du nicht dereferenzieren den letzten von denen)
  • wenn Ihre Plattform/Hardware/was auch immer Einschränkungen hinsichtlich des Speicherorts von Daten aufweist, werden diese von x erfüllt
7
Useless

Zuweisen von Speicher bedeutet, das Betriebssystem nach Speicher zu fragen. Dies bedeutet, dass es das Programm selbst ist, das nur dann nach "Speicherplatz" in RAM) fragt, wenn es benötigt wird. Zum Beispiel, wenn Sie ein Array verwenden möchten, dessen Größe Sie vorher jedoch nicht kennen Wenn das Programm ausgeführt wird, können Sie zwei Dinge tun: - Deklarieren und Anordnen [x] mit x, das von Ihnen festgelegt wurde, und zwar beliebig lang. Zum Beispiel 100. Aber was ist, wenn Ihr Programm nur ein Array mit 20 Elementen benötigt? Sie verschwenden Speicher für nichts - dann können Sie ein Array von x Elementen programmieren, wenn es die richtige Größe von x kennt. Programme im Speicher sind in 4 Segmente unterteilt: - Stapel (für den Aufruf von Funktionen erforderlich) - Code (der ausführbare Code der Bibliothek) - Daten (globale Variablen/Daten) - Heap, in diesem Segment finden Sie den zugewiesenen Speicher. Wenn Sie feststellen, dass Sie den zugewiesenen Speicher nicht mehr benötigen, geben Sie ihn an das Betriebssystem zurück.

Wenn Sie 10 Ganzzahlen zuweisen und anordnen möchten, gehen Sie wie folgt vor:

int * array = (int *) malloc (sizeof (int) * 10)

Und dann gibst du es mit free (array) an das OS zurück

3
user2204592