Bestimmen Sie die Größe des dynamisch zugewiesenen Speichers in C

Lesezeit: 9 Minuten

Benutzeravatar von s_itbhu
s_itbhu

Gibt es in C eine Möglichkeit, die Größe des dynamisch zugewiesenen Speichers herauszufinden?

Zum Beispiel danach

char* p = malloc (100);

Gibt es eine Möglichkeit, die Größe des damit verbundenen Speichers herauszufinden? p?

  • sizeof(char) * … ist überflüssig, wie char hat garantiert eine Größe von 1.

    – mk12

    19. August 2012 um 4:50 Uhr


  • @ mk12 Es macht immer noch klarer, was los ist. Vor allem, wenn geschrieben als malloc(100*sizeof(char))was der üblichen Konvention folgt, Einheiten auf der rechten Seite einer Menge zu platzieren.

    – DeprimiertDaniel

    29. Dezember 2016 um 4:10 Uhr

  • Eigentlich schreibe ich jetzt lieber TYPE *ptr = malloc(100 * sizeof *ptr), wobei TYPE nur einmal geschrieben wird. Dies garantiert, dass Sie ein Array von 100 Elementen erhalten, selbst wenn Sie TYPE ändern.

    – mk12

    30. Dezember 2016 um 20:41 Uhr

Benutzeravatar von ars
Ars

Es gibt keine Standardmethode, um diese Informationen zu finden. Einige Implementierungen bieten jedoch Funktionen wie msize um dies zu tun. Zum Beispiel:

Denken Sie jedoch daran, dass malloc ein Minimum der angeforderten Größe zuweist, also sollten Sie überprüfen, ob die msize-Variante für Ihre Implementierung tatsächlich die Größe des Objekts oder den tatsächlich auf dem Heap zugewiesenen Speicher zurückgibt.

Benutzeravatar von Alex Reynolds
Alex Reynolds

comp.lang.c FAQ-Liste · Frage 7.27

Q. So kann ich das abfragen malloc Paket, um herauszufinden, wie groß ein zugewiesener Block ist?

A. Leider gibt es keine standardisierte oder tragbare Methode. (Einige Compiler bieten nicht standardmäßige Erweiterungen.) Wenn Sie es wissen müssen, müssen Sie es selbst im Auge behalten. (Siehe auch Frage 7.28.)

Die C-Mentalität besteht darin, dem Programmierer Werkzeuge zur Verfügung zu stellen, die ihm bei seiner Arbeit helfen, und nicht Abstraktionen bereitzustellen, die die Natur seiner Arbeit verändern. C versucht auch zu vermeiden, Dinge einfacher/sicherer zu machen, wenn dies zu Lasten der Leistungsgrenze geht.

Bestimmte Dinge, die Sie vielleicht gerne mit einem Speicherbereich machen möchten, erfordern nur die Position des Beginns des Bereichs. Solche Dinge beinhalten das Arbeiten mit nullterminierten Strings, das Manipulieren des ersten n Bytes der Region (wenn bekannt ist, dass die Region mindestens so groß ist) und so weiter.

Grundsätzlich ist es zusätzliche Arbeit, die Länge einer Region zu verfolgen, und wenn C es automatisch tun würde, würde es es manchmal unnötig tun.

Viele Bibliotheksfunktionen (z fread()) erfordern einen Zeiger auf den Beginn einer Region sowie die Größe dieser Region. Wenn Sie die Größe einer Region benötigen, müssen Sie den Überblick behalten.

Ja, malloc()-Implementierungen verfolgen normalerweise die Größe einer Region, aber sie können dies indirekt tun oder sie auf einen bestimmten Wert aufrunden oder sie überhaupt nicht behalten. Selbst wenn sie es unterstützen, kann es langsam sein, die Größe auf diese Weise zu finden, verglichen damit, sie selbst im Auge zu behalten.

Wenn Sie eine Datenstruktur benötigen, die weiß, wie groß jede Region ist, kann C das für Sie tun. Verwenden Sie einfach eine Struktur, die verfolgt, wie groß die Region ist, sowie einen Zeiger auf die Region.

  • Obwohl diese Antwort die Frage nicht ganz beantwortet, schätze ich die Erklärung der Begründung dafür, dass so etwas nicht existiert.

    – Beschichteter Elch

    24. September 2013 um 3:34 Uhr

  • Wenn der Compiler keine Aufzeichnungen darüber hat, wie groß ein Block ist, was macht er dann beim Matching Free?

    – Andreas Lazarus

    13. Februar 2015 um 18:19 Uhr

  • @AndrewLazarus Die Bibliotheksimplementierung kann es möglicherweise indirekt bestimmen, ohne dem Block direkt etwas hinzuzufügen, und es könnte sich weigern, es zu teilen 🙂

    – Kuba hat Monica nicht vergessen

    16. August 2015 um 7:53 Uhr

  • Es könnte argumentiert werden, dass angesichts einer solchen “indirekten Art der Bestimmung” es existiert, und ich kann mir nicht vorstellen, wie es nicht sein könnte, dass dies nützliche Informationen in der Bibliothek sind, zu denen die Bibliothek Zugang bieten sollte. Sonst ist es so, als würde die Bibliothek (= Bibliotheksentwickler) den Bibliotheksnutzer verspotten (“Klar, das muss ich wissen, aber ich sage es dir nicht, du musst selbst den Überblick behalten, nur weil ich keine Lust habe!”) Ich halte es für einen grundlegenden Fehler in der C-Bibliothek.

    – LHP

    6. Mai um 12:28 Uhr

Benutzeravatar von Leslie Godwin
Leslie Godwin

Hier ist der beste Weg, den ich gesehen habe, um einen markierten Zeiger zu erstellen, um die Größe mit der Adresse zu speichern. Alle Zeigerfunktionen würden weiterhin wie erwartet funktionieren:

Gestohlen von: https://stackoverflow.com/a/35326444/638848

Sie könnten auch einen Wrapper für malloc implementieren und Tags (wie zugewiesene Größe und andere Metainformationen) vor dem von malloc zurückgegebenen Zeiger hinzufügen. Dies ist tatsächlich die Methode, mit der ein C++-Compiler Objekte mit Verweisen auf virtuelle Klassen markiert. Hier ist ein funktionierendes Beispiel:

#include <stdlib.h>
#include <stdio.h>

void * my_malloc(size_t s) 
{
  size_t * ret = malloc(sizeof(size_t) + s);
  *ret = s;
  return &ret[1];
}

void my_free(void * ptr) 
{
  free( (size_t*)ptr - 1);
}

size_t allocated_size(void * ptr) 
{
  return ((size_t*)ptr)[-1];
}

int main(int argc, const char ** argv) {
  int * array = my_malloc(sizeof(int) * 3);
  printf("%u\n", allocated_size(array));
  my_free(array);
  return 0;
}

Der Vorteil dieser Methode gegenüber einer Struktur mit Größe und Zeiger

 struct pointer
 {
   size_t size;
   void *p;
 };

ist, dass du nur das malloc ersetzen musst und kostenlos anrufst. Alle anderen Zeigeroperationen erfordern kein Refactoring.

Nein, die C-Laufzeitbibliothek stellt eine solche Funktion nicht zur Verfügung.

Einige Bibliotheken können plattform- oder compilerspezifische Funktionen bereitstellen, die diese Informationen abrufen können, aber im Allgemeinen ist der Weg, diese Informationen zu verfolgen, in einer anderen Integer-Variablen.

Jeder, der Ihnen sagt, dass es unmöglich ist, ist technisch korrekt (die beste Art von Korrektheit).

Aus technischen Gründen ist es keine gute Idee, sich auf das malloc-Subsystem zu verlassen, um Ihnen die genaue Größe eines zugewiesenen Blocks mitzuteilen. Um sich davon zu überzeugen, stellen Sie sich vor, Sie schreiben a groß Anwendung, mit mehreren verschiedenen Speicherzuweisungen – vielleicht verwenden Sie raw libc malloc in einem Teil, aber C++ operator new in einem anderen Teil und dann eine bestimmte Windows-API in einem weiteren Teil. Sie haben also alle möglichen void* herum fliegen. Schreiben einer Funktion, an der gearbeitet werden kann irgendein von diesen void*Das ist unmöglich, es sei denn, Sie können anhand des Zeigerwerts irgendwie erkennen, von welchem ​​Ihrer Haufen er stammt.

Daher möchten Sie vielleicht jeden Zeiger in Ihrem Programm mit einer Konvention zusammenfassen, die angibt, woher der Zeiger stammt (und wohin er zurückgegeben werden muss). In C++ nennen wir das zum Beispiel std::unique_ptr<void> (für Zeiger, die sein müssen operator delete‘d) oder std::unique_ptr<void, D> (für Zeiger, die über einen anderen Mechanismus zurückgegeben werden müssen D). Sie könnten dasselbe in C tun, wenn Sie wollten. Und sobald Sie Zeiger in größere, sicherere Objekte einpacken ohnehines ist nur ein kleiner Schritt zu struct SizedPtr { void *ptr; size_t size; } und dann brauchen Sie sich nie wieder Gedanken über die Größe einer Zuteilung zu machen.

Jedoch.

Es gibt Auch gute Gründe, warum Sie berechtigterweise die tatsächliche zugrunde liegende Größe einer Allokation wissen möchten. Vielleicht schreiben Sie beispielsweise ein Profiling-Tool für Ihre App, das die tatsächliche Speichermenge meldet Gebraucht von jedem Subsystem, nicht nur die Menge an Speicher, die der Programmierer Gedanke er benutzte. Wenn jede Ihrer 10-Byte-Zuweisungen heimlich 16 Bytes unter der Haube verwendet, ist das gut zu wissen! (Natürlich wird es auch andere Gemeinkosten geben, die Sie auf diese Weise nicht messen. Aber es gibt noch andere Werkzeuge dafür das Job.) Oder vielleicht untersuchen Sie nur das Verhalten von realloc auf Ihrer Plattform. Oder vielleicht möchten Sie die Kapazität einer wachsenden Zuweisung „aufrunden“, um sie zu vermeiden verfrüht Umverteilungen in der Zukunft. Beispiel:

SizedPtr round_up(void *p) {
    size_t sz = portable_ish_malloced_size(p);
    void *q = realloc(p, sz);  // for sanitizer-cleanliness
    assert(q != NULL && portable_ish_malloced_size(q) == sz);
    return (SizedPtr){q, sz};
}
bool reserve(VectorOfChar *v, size_t newcap) {
    if (v->sizedptr.size >= newcap) return true;
    char *newdata = realloc(v->sizedptr.ptr, newcap);
    if (newdata == NULL) return false;
    v->sizedptr = round_up(newdata);
    return true;
}

Um die Größe der Zuordnung hinter einem Nicht-Null-Zeiger abzurufen die direkt von libc malloc zurückgegeben wurde – nicht von einem benutzerdefinierten Heap und nicht in die Mitte eines Objekts zeigend – können Sie die folgenden betriebssystemspezifischen APIs verwenden, die ich der Einfachheit halber in einer „portable“ Wrapper-Funktion gebündelt habe. Wenn Sie ein gängiges System finden, bei dem dieser Code nicht funktioniert, hinterlassen Sie bitte einen Kommentar und ich werde versuchen, es zu beheben!

#if defined(__linux__)
// https://linux.die.net/man/3/malloc_usable_size
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_usable_size((void*)p);
}
#elif defined(__APPLE__)
// https://www.unix.com/man-page/osx/3/malloc_size/
#include <malloc/malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_size(p);
}
#elif defined(_WIN32)
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return _msize((void *)p);
}
#else
#error "oops, I don't know this system"
#endif

#include <stdio.h>
#include <stdlib.h>  // for malloc itself

int main() {
    void *p = malloc(42);
    size_t true_length = portable_ish_malloced_size(p);
    printf("%zu\n", true_length);
}

Getestet am:

  • Visual Studio, Win64 — _msize
  • GCC/Clang, glibc, Linux — malloc_usable_size
  • Clang, libc, Mac OS X — malloc_size
  • Clang, jemalloc, Mac OS X – funktioniert in der Praxis, aber ich würde ihm nicht vertrauen (mischt leise jemallocs malloc und die nativen libcs malloc_size)
  • Sollte damit gut funktionieren jemalloc unter Linux
  • Sollte damit gut funktionieren dlmalloc unter Linux wenn ohne kompiliert USE_DL_PREFIX
  • Sollte damit gut funktionieren tcmalloc überall

Wie alle anderen schon sagten: Nein gibt es nicht.

Außerdem würde ich hier immer alle herstellerspezifischen Funktionen vermeiden, denn wenn Sie feststellen, dass Sie sie wirklich verwenden müssen, ist dies im Allgemeinen ein Zeichen dafür, dass Sie es falsch machen. Die Größe sollte man entweder separat hinterlegen, oder gar nicht kennen müssen. Die Verwendung von Herstellerfunktionen ist der schnellste Weg, um einen der Hauptvorteile des Schreibens in C, die Portabilität, zu verlieren.

  • Ich habe einige Projekte gesehen, aber es gab nur eines, bei dem die Idee “das sollte auf etwas anderem laufen als dem, wofür ursprünglich entworfen wurde” aufgekommen ist. Jedes beteiligte Projekt im Unternehmen wurde beauftragt, diesen Punkt von Grund auf neu zu schreiben.

    – Kobor42

    23. Mai 2013 um 9:52 Uhr

  • Dann hast du Glück gehabt. Ich arbeite an vielen Legacy-Projekten, und diese Art von Problemen sind in der Regel sehr schwer zu lösen.

    – Enno

    26. Oktober 2020 um 9:48 Uhr

1418570cookie-checkBestimmen Sie die Größe des dynamisch zugewiesenen Speichers in C

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

Privacy policy