Woher weiß frei, wie viel frei ist?


385

In der C-Programmierung können Sie jede Art von Zeiger, die Sie möchten, als Argument an free übergeben. Woher weiß es, wie groß der zugewiesene Speicher ist, der freigegeben werden soll? Immer wenn ich einen Zeiger auf eine Funktion übergebe, muss ich auch die Größe übergeben (dh ein Array von 10 Elementen muss 10 als Parameter erhalten, um die Größe des Arrays zu kennen), aber ich muss die Größe nicht an die übergeben freie Funktion. Warum nicht, und kann ich dieselbe Technik in meinen eigenen Funktionen verwenden, um zu vermeiden, dass ich die zusätzliche Variable der Array-Länge umfahren muss?


Eine ähnliche Frage: stackoverflow.com/questions/851958/… (obwohl ich sagen würde, dass es nicht ganz doppelt ist)
John Carter

Das Buddy-System ist eine weitere Möglichkeit, die anhand des Zeigers ohne Overhead in jedem Block ermittelt werden kann.
EvilTeach

Antworten:


349

Wenn Sie anrufen malloc(), geben Sie die zuzuweisende Speichermenge an. Die tatsächlich verwendete Speichermenge ist geringfügig höher und enthält zusätzliche Informationen, die (zumindest) aufzeichnen, wie groß der Block ist. Sie können nicht (zuverlässig) auf diese anderen Informationen zugreifen - und sollten Sie auch nicht :-).

Wenn Sie anrufen free(), werden einfach die zusätzlichen Informationen angezeigt, um herauszufinden, wie groß der Block ist.


44
Zu Ihrer Information, zum Beispiel muss BSD malloc_size()zuverlässig über einen malloc()ed-Zeiger auf die Blockgröße zugreifen . Aber es gibt keinen zuverlässigen, tragbaren Weg.
Laalto

50
Ich denke, es ist wichtig zu sagen, dass sich dieser zusätzliche Informationsblock vor dem zurückgegebenen Zeiger befindet.
Georg Schölly

39
@gs Nun, das ist implementierungsabhängig. Aber ja, dort ist es normalerweise.
Falaina

31
Können Sie sich den Horror vorstellen, wenn free()der Programmierer genau angeben muss, wie groß der malloc()Block war? Die Speicherlecks sind so schlimm wie sie sind.
MusiGenesis

35
Warum sind diese Informationen für malloc()und verfügbar free(), aber Sie müssen die Größe eines Arrays speichern? Warum würden sie es nicht möglich machen, so etwas zu tun, blockSize(ptr)wenn sie die Informationen trotzdem speichern?
CorsiKa

144

Die meisten Implementierungen von C-Speicherzuweisungsfunktionen speichern Abrechnungsinformationen für jeden Block entweder inline oder separat.

Ein typischer Weg (inline) besteht darin, sowohl einen Header als auch den von Ihnen angeforderten Speicher zuzuweisen, der auf eine Mindestgröße aufgefüllt ist. Wenn Sie beispielsweise nach 20 Bytes gefragt haben, weist das System möglicherweise einen 48-Byte-Block zu:

  • 16-Byte-Header mit Größe, Spezialmarkierung, Prüfsumme, Zeigern auf den nächsten / vorherigen Block usw.
  • 32-Byte-Datenbereich (Ihre 20 Byte werden auf ein Vielfaches von 16 aufgefüllt).

Die Ihnen dann gegebene Adresse ist die Adresse des Datenbereichs. Wenn Sie den Block freefreigeben, nehmen Sie einfach die Adresse, die Sie ihm gegeben haben, und überprüfen Sie die Buchhaltungsinformationen unmittelbar davor, vorausgesetzt, Sie haben diese Adresse oder den Speicher um ihn herum nicht vollgestopft. Grafisch wäre das wie folgt:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Beachten Sie, dass die Größe des Headers und des Auffüllens vollständig implementierungsdefiniert sind (tatsächlich ist das Ganze implementierungsdefiniert (a) aber die Inline-Abrechnungsoption ist eine übliche).

Die Prüfsummen und speziellen Markierungen in den Buchhaltungsinformationen sind häufig die Ursache für Fehler wie "Speicherbereich beschädigt" oder "Doppelt frei", wenn Sie sie überschreiben oder zweimal freigeben.

Das Auffüllen (um die Zuordnung effizienter zu gestalten) ist der Grund, warum Sie manchmal ein wenig über das Ende Ihres angeforderten Speicherplatzes hinaus schreiben können, ohne Probleme zu verursachen (tun Sie das trotzdem nicht, es ist undefiniertes Verhalten und, nur weil es manchmal funktioniert, nicht) Ich meine, es ist okay, es zu tun.


(a) Ich habe Implementierungen von geschriebenmalloc in eingebetteten Systemen bei denen Sie 128 Bytes erhalten haben, unabhängig davon, wonach Sie gefragt haben (das war die Größe der größten Struktur im System), vorausgesetzt, Sie haben nach 128 Bytes oder weniger gefragt (Anfragen nach mehr würden mit einem NULL-Rückgabewert erfüllt sein). Eine sehr einfache Bitmaske (dh nicht inline) wurde verwendet, um zu entscheiden, ob ein 128-Byte-Block zugewiesen wurde oder nicht.

Andere, die ich entwickelt habe, hatten unterschiedliche Pools für 16-Byte-Chunks, 64-Byte-Chunks, 256-Byte-Chunks und 1K-Chunks, wobei wiederum eine Bitmaske verwendet wurde, um zu entscheiden, welche Blöcke verwendet wurden oder verfügbar waren.

Beiden Optionen verwalten den Aufwand der Accounting - Informationen zu reduzieren und die Geschwindigkeit zu erhöhen mallocund free(keine Notwendigkeit zu benachbarten Blöcke zusammenwachsen , wenn Befreiung), besonders wichtig in der Umgebung , die wir in arbeiten.


@paxdiablo Bedeutet das, dass malloc keine zusammenhängenden Speicherblöcke zuweist?
user10678

2
@ user10678, die einzige wirkliche Anforderung von mallocist, dass Sie für den erfolgreichen Fall einen Speicherblock erhalten, der mindestens so groß ist, wie Sie es gewünscht haben. Einzelne Blöcke sind in Bezug auf den Zugriff auf Elemente in ihnen zusammenhängend, es ist jedoch nicht erforderlich, dass die Arenen, aus denen die Blöcke stammen, zusammenhängend sind.
Paxdiablo

Verwandte Frage: Warum gibt es keine Variation von malloc / free, bei der Sie die Größe beim Freigeben angeben und die Größe nicht speichern müssen?
user253751

@ user253751, denn dann ist theer eine weitere Sache, die Sie über den Zeiger selbst hinaus verfolgen müssen. Es ist sowohl unnötig als auch gefährlich: void *x = malloc(200); free(x, 500);wird nicht gut enden :-) Aus Effizienzgründen kann die tatsächliche Größe des Puffers in jedem Fall größer sein (darauf können Sie sich einfach nicht verlassen).
Paxdiablo

@paxdiablo Es wird auch vermieden, Speicher zu verschwenden, um die Größe zu halten.
user253751

47

Aus der comp.lang.cFAQ-Liste: Woher weiß free, wie viele Bytes frei sind?

Die malloc / free-Implementierung speichert die Größe jedes zugewiesenen Blocks, sodass es beim Freigeben nicht erforderlich ist, ihn an die Größe zu erinnern. (Normalerweise wird die Größe neben dem zugewiesenen Block gespeichert, weshalb die Dinge normalerweise schlecht brechen, wenn die Grenzen des zugewiesenen Blocks sogar leicht überschritten werden.)


2
Dies ist keine Antwort. Die Frage ist genau die folgende: Warum kann free die Größe des Blocks zuverlässig nachschlagen, aber dem Programmierer, der dies tut, steht noch keine Funktion zur Verfügung?
Bananach

Dies ist in der Tat ein Implementierungsdetail für die Malloc-API, und es gibt keine API, um diese Informationen (meines Wissens) auf standardmäßige Weise zurückzugewinnen. Das "System" zeichnet es auf und verwendet es weiter free. Vielleicht befriedigt Sie die Antwort nicht, aber ich glaube nicht, dass Sie eine mit allgemeiner anwendbaren Informationen erhalten :-)
jdehaan

6

Diese Antwort wird verschoben von Woher weiß free (), wie viel Speicher freigegeben werden muss? wo ich durch eine scheinbar doppelte Frage daran gehindert wurde, zu antworten. Diese Antwort sollte dann für dieses Duplikat relevant sein:


Für den Fall von mallocspeichert der Heap-Allokator eine Zuordnung des ursprünglich zurückgegebenen Zeigers zu relevanten Details, freedie später für den Speicher benötigt werden . Dies beinhaltet typischerweise das Speichern der Größe des Speicherbereichs in der für den verwendeten Allokator relevanten Form, beispielsweise der Rohgröße, oder eines Knotens in einem Binärbaum, der zum Verfolgen von Zuordnungen verwendet wird, oder einer Anzahl von verwendeten Speichereinheiten.

freeschlägt nicht fehl, wenn Sie den Zeiger "umbenennen" oder in irgendeiner Weise duplizieren. Es wird jedoch keine Referenz gezählt, und nur die erste freeist korrekt. Zusätzliche frees sind "doppelt freie" Fehler.

Der Versuch, freeeinen Zeiger mit einem anderen Wert als dem von mallocs zuvor zurückgegebenen und noch nicht freigegebenen Wert zu verwenden , ist ein Fehler. Es ist nicht möglich, Speicherbereiche, von denen zurückgegeben wird, teilweise freizugeben malloc.


Ich habe den Wert eines Zeigers geändert, der von einem Malloc-Aufruf zurückgegeben wurde. Und ich habe es ohne Fehler befreit. Warum? Siehe hier: stackoverflow.com/questions/42618390/…
smwikipedia

4

In einem ähnlichen Zusammenhang verfügt die GLib- Bibliothek über Speicherzuweisungsfunktionen, die keine implizite Größe speichern - und dann übergeben Sie einfach den Parameter size an free. Dies kann einen Teil des Overheads eliminieren.


3

malloc()und free()sind system- / compilerabhängig, so dass es schwierig ist, eine bestimmte Antwort zu geben.

Weitere Informationen zu dieser anderen Frage .


2
Sie sind wirklich bibliotheksabhängig (normalerweise die C-Bibliothek, die normalerweise sehr eng mit dem Betriebssystem verbunden ist). Für den Compiler sind sie nur Funktionen.
Donal Fellows

2

Der Heap-Manager hat die Speichermenge, die zu dem zugewiesenen Block gehört, irgendwo gespeichert, als Sie aufgerufen haben malloc.

Ich habe selbst nie einen implementiert, aber ich denke, der Speicher direkt vor dem zugewiesenen Block enthält möglicherweise die Metainformationen.


3
Dies ist eine mögliche Implementierung, aber man könnte ein System entwickeln, bei dem der gesamte Speicher in einer einzigen Tabelle auf einer völlig anderen Seite verfolgt wird, nicht unbedingt in der Nähe des Speicherpools, aus dem zugewiesen wird.
Ephemient

2

Die ursprüngliche Technik bestand darin, einen etwas größeren Block zuzuweisen und die Größe am Anfang zu speichern, um dann der Anwendung den Rest des Blogs zu geben. Der zusätzliche Platz hat eine Größe und möglicherweise Links, um die freien Blöcke zur Wiederverwendung zusammenzufädeln.

Es gibt jedoch bestimmte Probleme mit diesen Tricks, wie z. B. schlechtes Cache- und Speicherverwaltungsverhalten. Die Verwendung von Speicher direkt im Block führt dazu, dass Dinge unnötig eingeblendet werden, und es entstehen auch schmutzige Seiten, die das Teilen und das Kopieren beim Schreiben erschweren.

Eine fortgeschrittenere Technik besteht also darin, ein separates Verzeichnis zu führen. Es wurden auch exotische Ansätze entwickelt, bei denen Speicherbereiche dieselbe Zweierpotenzgröße verwenden.

Im Allgemeinen lautet die Antwort: Eine separate Datenstruktur wird zugewiesen, um den Status beizubehalten.


1

Um die zweite Hälfte Ihrer Frage zu beantworten: Ja, das können Sie, und ein ziemlich häufiges Muster in C ist das Folgende:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;

Das ist eine völlig andere Technik als die, die BSD Malloc für kleine Objekte verwendet (obwohl es eine perfekte Technik zum Erstellen von Arrays im Pascal-Stil ist)
Pete Kirkham,

0

Wenn wir malloc aufrufen, verbraucht es einfach mehr Byte von seiner Anforderung. Dieser Mehrbyteverbrauch enthält Informationen wie Prüfsumme, Größe und andere zusätzliche Informationen. Wenn wir zu diesem Zeitpunkt kostenlos anrufen, gehen Sie direkt zu den zusätzlichen Informationen, in denen die Adresse gefunden wird und wie viel Block frei sein wird.


0

Um die zweite Frage zu beantworten, können Sie die gleiche Technik anwenden, malloc() indem Sie einfach die erste Zelle in jedem Array der Größe des Arrays zuweisen. Auf diese Weise können Sie das Array senden, ohne ein zusätzliches Größenargument zu senden.

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.