Wenn free() die Länge meines Arrays kennt, warum kann ich sie dann nicht in meinem eigenen Code anfordern?

Lesezeit: 8 Minuten

Benutzeravatar von Chris Cooper
Chris Cooper

Ich weiß, dass es eine gängige Konvention ist, die Länge dynamisch zugewiesener Arrays an Funktionen zu übergeben, die sie manipulieren:

void initializeAndFree(int* anArray, size_t length);

int main(){
    size_t arrayLength = 0;
    scanf("%d", &arrayLength);
    int* myArray = (int*)malloc(sizeof(int)*arrayLength);

    initializeAndFree(myArray, arrayLength);
}

void initializeAndFree(int* anArray, size_t length){
    int i = 0;
    for (i = 0; i < length; i++) {
        anArray[i] = 0;
    }
    free(anArray);
}

aber wenn es für mich keine Möglichkeit gibt, die Länge des zugewiesenen Speichers von einem Zeiger zu erhalten, wie geht das? free() “automagisch” wissen, was ich freigeben soll, wenn ich nur denselben Zeiger gebe? Warum kann ich als C-Programmierer nicht in die Magie einsteigen?

Wo tut free() sein kostenloses (har-har) Wissen bekommen?

  • Beachte das auch int length ist falsch. Array-Längen und -Offsets und Typgrößen und andere solche Dinge sind vom Typ size_tdie in der definiert ist stddef.h, stdio.h, stdlib.hund string.h Kopfzeilen. Der Hauptunterschied zw size_t und int ist das int ist unterschrieben und size_t ist nicht signiert, aber auf einigen (z. B. 64-Bit-) Plattformen können sie auch unterschiedliche Größen haben. Sie sollten immer verwenden size_t.

    – Chris Lutz

    16. April 2010 um 6:21 Uhr

  • @Chris Lutz: Danke. Ich werde diese Änderung vornehmen. Ich habe das Suffix “_t” schon oft gesehen. Was bedeutet es? “Typ”? Wie in “size_type?” Welche anderen Beispiele gibt es?

    – Chris Cooper

    16. April 2010 um 10:52 Uhr

  • Ja, es steht für Typ. Es gibt viele andere Beispiele, einschließlich int32_t, regex_t, time_t, wchar_tetc.

    – Matthäus Flaschen

    16. April 2010 um 11:52 Uhr

  • Ein Typ mit _t Suffix ist ein Typ, der kein grundlegender Typ der Sprache ist. Was bedeutet, dass size_t ist unsigned int, unsigned long oder so ähnlich.

    – u0b34a0f6ae

    15. Mai 2010 um 17:19 Uhr

Benutzeravatar von Matthew Flaschen
Matthäus Flaschen

Abgesehen von Klatchkos korrektem Hinweis, dass der Standard dies nicht vorsieht, weisen echte malloc/free-Implementierungen oft mehr Platz zu, als Sie verlangen. Wenn Sie beispielsweise nach 12 Bytes fragen, können 16 bereitgestellt werden (siehe Ein Speicherzuordner, die anmerkt, dass 16 eine übliche Größe ist). Es muss also nicht wissen, dass Sie nach 12 Bytes gefragt haben, nur dass es gab dir ein 16-Byte-Stück.

  • Aber was ist mit C++? In C++ kennt die Laufzeit beim Allokieren mit die tatsächliche Größe new type[n] wie es n Konstruktoren aufruft delete []?

    – Viktor Sehr

    16. April 2010 um 11:25 Uhr

  • @Viktor Es muss die Größe für primitive Typen oder Typen mit einem leeren Destruktor nicht gespeichert werden.

    – Yacoby

    16. April 2010 um 11:29 Uhr


  • @Yacoby: Stimmt, es beantwortet meine Frage immer noch nicht

    – Viktor Sehr

    16. April 2010 um 12:06 Uhr

  • Viktor, Ihre Frage ist also, warum bietet C++ keine Möglichkeit, die Anzahl der Elemente in einem Array abzurufen, wenn alle Elemente nicht leere Destruktoren haben? Wahrscheinlich, weil es etwas verwirrend wäre (Sie müssen Implementierungsdetails kennen, damit eine Klasse weiß, ob die Funktion sicher ist) und kein notwendiges Feature ist. Aber dies verdient eine eigene Frage (die möglicherweise bereits existiert).

    – Matthäus Flaschen

    16. April 2010 um 12:28 Uhr


  • @ViktorSehr: Wenn eine Implementierung mehr Speicher als angefordert zuweist und auf beliebige Weise feststellen kann, dass das Aufrufen eines Destruktors für diesen Speicher keine sichtbaren Nebenwirkungen hat, ist es nicht erforderlich, die tatsächliche Zuweisung zu verfolgen. Es wäre sehr verwirrend, eine Funktion zum Anfordern der tatsächlichen Größe einer Zuweisung zu haben, die jedoch nicht funktioniert, wenn ein Compiler feststellt, dass er diese Informationen nicht aufbewahren muss.

    – Superkatze

    14. Oktober 2016 um 18:20 Uhr

Sie können es nicht bekommen, weil das C-Komitee das im Standard nicht verlangt hat.

Wenn Sie bereit sind, nicht portierbaren Code zu schreiben, haben Sie vielleicht Glück mit:

*((size_t *)ptr - 1)

oder vielleicht:

*((size_t *)ptr - 2)

Aber ob das funktioniert, hängt davon ab, wo genau die Implementierung von malloc, die Sie verwenden, diese Daten speichert.

  • Ich würde vereinfachen ((size_t *)ptr)[-1] persönlich.

    – Chris Lutz

    16. April 2010 um 6:18 Uhr

  • @Chris Lutz: Was bedeutet [-1] angeben?

    – Chris Cooper

    16. April 2010 um 10:57 Uhr

  • Es gibt vor, der Zeiger sei ein Array und indiziert das Element eins vor dem Start.

    – Simon Buchan

    16. April 2010 um 11:23 Uhr

  • @Simon: Ups. DUH. Vielen Dank. Ich dachte, das würde einen Typ deklarieren. Ich glaube, meine Augen wurden glasig, als ich all die * und (‘s. =P

    – Chris Cooper

    16. April 2010 um 12:10 Uhr

  • Können Sie erläutern, auf welchen Plattformen dies voraussichtlich funktionieren wird?

    – einpoklum

    12. Dezember 2013 um 10:18 Uhr

Benutzeravatar von N 1.1
N 1.1

Nachdem ich die Antwort von Klatchko gelesen hatte, versuchte ich es selbst und ptr[-1] tatsächlich speichert die tatsächlich Speicher (normalerweise mehr als der Speicher, um den wir gebeten haben, wahrscheinlich um gegen Segmentierungsfehler zu sparen).

{
  char *a = malloc(1);
  printf("%u\n", ((size_t *)a)[-1]);   //prints 17
  free(a);
  exit(0);
}

Beim Versuch mit verschiedenen Größen weist GCC den Speicher wie folgt zu:

Der anfänglich zugewiesene Speicher beträgt 17 Bytes.
Der zugewiesene Speicher ist mindestens 5 Bytes größer als die angeforderte Größe, wenn mehr angefordert wird, werden 8 Bytes mehr zugewiesen.

  • Wenn Größe ist [0,12]zugewiesener Speicher ist 17.
  • Wenn Größe ist [13]der zugewiesene Speicher beträgt 25.
  • Wenn Größe ist [20]der zugewiesene Speicher beträgt 25.
  • Wenn Größe ist [21]zugewiesener Speicher ist 33.

  • Kühl! Vielen Dank für die Erklärung.

    – Chris Cooper

    16. April 2010 um 12:09 Uhr

  • Beachten Sie, dass ein anderer Zuordner die Größe möglicherweise an einer anderen Stelle speichert. Vielleicht teilt sich eine ganze Gruppe von Zuweisungen eine 64-KB-Arena mit der Größe, die im ersten Block der Arena gespeichert ist.

    – Zan Luchs

    20. März 2012 um 21:48 Uhr

  • Das ist eine völlig irreführende Antwort. GCC weist nichts zu. Ihre C-Bibliothek tut das (oder sogar das Betriebssystem). Reverse Engineering einer Implementierung wie dieser ist Programmieren durch Experimentierenwas links rechts und in der Mitte scheitern muss.

    – Jens

    7. Mai 2014 um 13:27 Uhr

Benutzeravatar von Clifford
Clifford

Obwohl es möglich ist, die Metadaten zu erhalten, die der Speicherzuordner vor dem zugewiesenen Block platziert, würde dies nur funktionieren wenn der Zeiger ist wirklich ein Zeiger auf einen dynamisch zugewiesenen Block. Dies würde die Nützlichkeit der Funktion ernsthaft beeinträchtigen, die erfordert, dass alle übergebenen Argumente Zeiger auf solche Blöcke sind, anstatt ein einfaches automatisches oder statisches Array zu sagen.

Der Punkt ist, dass es keinen tragbaren Weg von der Inspektion des Zeigers gibt, um zu wissen, auf welche Art von Speicher er zeigt. Obwohl es also eine interessante Idee ist, ist es keine besonders sichere Angelegenheit.

Eine sichere und tragbare Methode wäre, das erste Wort der Zuordnung zu reservieren, um die Länge zu halten. GCC (und vielleicht einige andere Compiler) unterstützt eine nicht-portable Methode, dies zu implementieren, indem eine Struktur mit einem Array der Länge Null verwendet wird, was den Code im Vergleich zu einer portablen Lösung etwas vereinfacht:

typedef struct
{
    size_t length ;
    char alloc[0] ;   // Compiler specific extension!!!
} tSizedAlloc ;

// Allocating a sized block
tSizedAlloc* blk = malloc( sizeof(tSizedAlloc) + length ) ;
blk->length = length ;

// Accessing the size and data information of the block
size_t blk_length = blk->length ;
char*  data = blk->alloc ;

Ich weiß, dieser Thread ist schon etwas älter, aber ich habe trotzdem etwas zu sagen. Es gibt eine Funktion (oder ein Makro, ich habe die Bibliothek noch nicht überprüft) malloc_usable_size() – erhält die Größe des vom Heap zugewiesenen Speicherblocks. Die Manpage gibt an, dass es nur zum Debuggen dient, da es nicht die Nummer ausgibt, die Sie gefragt haben, sondern die Nummer, die es zugewiesen hat, die etwas größer ist. Beachten Sie, dass es sich um eine GNU-Erweiterung handelt.

Andererseits wird es vielleicht nicht einmal benötigt, weil ich glaube, dass man seine Größe nicht kennen muss, um einen Speicherblock freizugeben. Entfernen Sie einfach das Handle/den Deskriptor/die Struktur, die für den Chunk verantwortlich ist.

Benutzeravatar von sharptooth
scharfer Zahn

Eine nicht standardmäßige Methode ist die Verwendung _msize(). Durch die Verwendung dieser Funktion wird Ihr Code nicht portierbar. Auch die Dokumentation ist nicht sehr klar, ob sie die übergebene Nummer zurückgibt malloc() oder die tatsächliche Blockgröße (möglicherweise größer).

Es liegt an der malloc Implementierer, wie diese Daten gespeichert werden. Meistens wird die Länge direkt vor dem zugewiesenen Speicher gespeichert (d. h. wenn Sie 7 Bytes zuweisen möchten, werden in Wirklichkeit 7+x Bytes zugewiesen, wobei die x zusätzlichen Bytes zum Speichern der Metadaten verwendet werden). Manchmal werden die Metadaten sowohl vor als auch nach dem zugewiesenen Speicher gespeichert, um auf Heap-Beschädigungen zu prüfen. Der Implementierer kann sich jedoch auch dafür entscheiden, eine zusätzliche Datenstruktur zum Speichern der Metadaten zu verwenden.

  • Ich glaube, die Länge muss vorne gespeichert werden. Sie müssen die Größe des Puffers kennen, um zu wissen, wo Sie nachfolgende Metadaten finden. Wenn die Größe in den nachfolgenden Metadaten enthalten ist, haben Sie ein Henne-Ei-Problem, um an diese Daten zu gelangen.

    – R. Samuel Klatschko

    16. April 2010 um 6:05 Uhr

  • Nicht ‘muss’. Denkbar ist, dass ein Allokator eine Hashtabelle separat speichern kann, die Zeiger kostenlos auf Größen abbildet. Alternative: Haben Sie eine große Anzahl von Arenen mit unterschiedlichen Bucket-Größen, die Zuordnungsgröße kann basierend darauf bestimmt werden, ob der Zeiger in die Region dieser Arena fällt

    – Demur Rumed

    10. Oktober 2016 um 20:47 Uhr


1408930cookie-checkWenn free() die Länge meines Arrays kennt, warum kann ich sie dann nicht in meinem eigenen Code anfordern?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy