it-swarm.com.de

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

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

Zum Beispiel nach

char* p = malloc (100);

Gibt es eine Möglichkeit, die Größe des mit p verbundenen Speichers zu ermitteln?

50
s_itbhu

comp.lang.c FAQ Liste · Frage 7.27

F. Kann ich also das Paket malloc abfragen, um herauszufinden, wie groß ein zugewiesener Block ist?

A. Leider gibt es keinen Standard- oder tragbaren Weg. (Einige Compiler bieten nicht standardmäßige Erweiterungen an.) Wenn Sie wissen müssen, müssen Sie dies selbst nachverfolgen. (Siehe auch Frage 7.28 .)

41
Alex Reynolds

Es gibt keine Standardmethode, um diese Informationen zu finden. Einige Implementierungen stellen jedoch Funktionen wie msize zur Verfügung. Zum Beispiel:

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

42
ars

Die Mentalität besteht darin, dem Programmierer Werkzeuge zur Verfügung zu stellen, die ihn bei seiner Arbeit unterstützen, und nicht Abstraktionen bereitzustellen, die die Art seiner Arbeit verändern. C versucht auch, die Dinge einfacher/sicherer zu machen, wenn dies auf Kosten der Leistungsgrenze geschieht.

Bestimmte Dinge, die Sie möglicherweise mit einem Speicherbereich machen möchten, erfordern nur den Ort des Anfangs des Bereichs. Dazu gehören das Arbeiten mit nullterminierten Zeichenfolgen, das Manipulieren der ersten n Bytes der Region (wenn bekannt ist, dass diese Region mindestens so groß ist) und so weiter.

Grundsätzlich ist es eine zusätzliche Arbeit, die Länge einer Region im Auge zu behalten, und wenn C dies automatisch erledigt, ist es manchmal unnötig.

Viele Bibliotheksfunktionen (zum Beispiel fread()) erfordern einen Zeiger auf den Anfang einer Region und auch 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 auf einen Wert aufrunden oder sie nicht beibehalten. Selbst wenn sie dies unterstützen, ist die Größe auf diese Weise zu finden, verglichen mit dem Nachverfolgen der Informationen, langsam.

Wenn Sie eine Datenstruktur benötigen, die weiß, wie groß die einzelnen Regionen sind, kann C dies für Sie tun. Verwenden Sie einfach eine Struktur, die die Größe der Region sowie einen Zeiger auf die Region aufzeichnet.

10
Artelius

Nein, die C-Laufzeitbibliothek bietet keine solche Funktion.

Einige Bibliotheken stellen möglicherweise plattform- oder compilerspezifische Funktionen bereit, mit denen diese Informationen abgerufen werden können. Im Allgemeinen können Sie diese Informationen jedoch auch in einer anderen Ganzzahlvariablen nachverfolgen.

7
Greg Hewgill

Dies 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önnen auch einen Wrapper für Malloc implementieren und die Tags .__ hinzufügen. (wie zugewiesene Größe und andere Metainformationen) vor dem Zeiger von malloc zurückgegeben. Dies ist in der Tat die Methode, die ein C++ - Compiler markiert Objekte mit Referenzen auf virtuelle Klassen. Hier arbeitet einer 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 Sie nur die Malloc und kostenlose Anrufe ersetzen müssen. Alles andere Zeigeroperationen erfordern kein Refactoring.

4
Leslie Godwin

Wie alle anderen schon gesagt haben: Nein, gibt es nicht.

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

3
Enno

Ich würde erwarten, dass dies von der Implementierung abhängt.
Wenn Sie die Header-Datenstruktur erhalten haben, können Sie sie wieder auf den Zeiger werfen und die Größe ermitteln.

2
nik

Nun, ich weiß, dass dies nicht Ihre spezifische Frage beantwortet, jedoch sozusagen über den Tellerrand hinaus gedacht. Es fällt mir ein, dass Sie wahrscheinlich nicht wissen müssen. Ok, ok, nein, ich meine nicht, dass Sie eine schlechte oder unorthodoxe Implementierung haben ... Ich meine, Sie (wahrscheinlich ohne Ihren Code zu betrachten) möchten wahrscheinlich nur wissen, ob Ihre Daten passen Wenn dies im zugewiesenen Speicher der Fall ist, ist diese Lösung möglicherweise besser. Es sollte nicht zu viel Aufwand bieten und wird Ihr "Anpassungsproblem" lösen, wenn Sie dies tatsächlich tun:

if ( p != (tmp = realloc(p, required_size)) ) p = tmp;

oder wenn Sie den alten Inhalt pflegen müssen:

if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size);

natürlich könnten Sie einfach Folgendes verwenden:

p = realloc(p, required_size);

und sei damit fertig.

1
Erich Horn

Dieser Code wird wahrscheinlich auf den meisten Windows-Installationen funktionieren:

template <class T>
int get_allocated_bytes(T* ptr)
{
 return *((int*)ptr-4);
}

template <class T>
int get_allocated_elements(T* ptr)
{
 return get_allocated_bytes(ptr)/sizeof(T);
}
1
H. Acker

Wenn Sie Malloc verwenden, können Sie die Größe nicht ermitteln.

Wenn Sie dagegen OS-API verwenden, um Speicher dynamisch zuzuordnen, wie z. B. Windows Heap-Funktionen , ist dies möglich.

1

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

Aus technischen Gründen ist es nicht ratsam, sich auf das malloc-Subsystem zu verlassen, um die Größe eines zugewiesenen Blocks genau zu bestimmen. Um sich davon zu überzeugen, stellen Sie sich vor, Sie schreiben eine große Anwendung mit mehreren unterschiedlichen Speicherzuordnungen - vielleicht verwenden Sie die unformatierte libc malloc in einem Teil, aber C++ operator new In ein anderer Teil und dann eine bestimmte Windows-API in einem weiteren Teil. Sie haben also alle Arten von void* Herumfliegen. Das Schreiben einer Funktion, die mit jedem dieser void* - Werte arbeiten kann, ist unmöglich, es sei denn, Sie können aus dem Wert des Zeigers irgendwie erkennen, von welchem ​​Ihrer Haufen er stammt.

Vielleicht möchten Sie jeden Zeiger in Ihrem Programm mit einer Konvention abschließen, die angibt, woher der Zeiger stammt (und wohin er zurückgegeben werden muss). In C++ nennen wir beispielsweise std::unique_ptr<void> (Für Zeiger, die operator delete Sein müssen) oder std::unique_ptr<void, D> (Für Zeiger, die über einen anderen Mechanismus zurückgegeben werden müssen) D ). Sie könnten das Gleiche in C tun, wenn Sie möchten. Und sobald Sie Zeiger in größere sicherere Objekte verpackt haben , ist es nur ein kleiner Schritt zu struct SizedPtr { void *ptr; size_t size; }, Und Sie müssen sich nie wieder um die Größe einer Zuordnung kümmern .

Allerdings.

Es gibt auch gute Gründe, warum Sie die tatsächliche zugrunde liegende Größe einer Allokation zu Recht wissen möchten. Möglicherweise schreiben Sie ein Profilerstellungstool für Ihre App, das die tatsächliche Speichermenge , die von jedem Subsystem verwendet wird , und nicht nur die Speichermenge, die der Programmierer verwendet ) dachte er würde benutzen. Wenn jede Ihrer 10-Byte-Zuweisungen heimlich 16 Byte unter der Haube belegt, ist das gut zu wissen! (Natürlich wird es auch andere Kosten geben, die Sie nicht auf diese Weise messen. Aber es gibt noch andere Tools für diesen Job.) Oder Sie untersuchen nur das Verhalten von realloc auf Ihrer Plattform. Oder Sie möchten die Kapazität einer wachsenden Zuweisung "aufrunden", um vorzeitige Neuzuweisungen in Zukunft zu vermeiden. 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 zu ermitteln, der direkt von libc malloc zurückgegeben wurde - nicht von einem benutzerdefinierten Heap und nicht in die Mitte eines Objekts - können Sie Verwenden Sie die folgenden betriebssystemspezifischen APIs, die ich der Einfachheit halber in einer Wrapper-Funktion "portable ish" zusammengefasst habe. Wenn Sie ein allgemeines System finden, in dem dieser Code nicht funktioniert, hinterlassen Sie bitte einen Kommentar und ich werde versuchen, ihn 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:

1
Quuxplusone

Dies kann funktionieren, ein kleines Update in Ihrem Code:

void* inc = (void*) (++p)
size=p-inc;

Dies führt jedoch zu 1, dh zu p gehörigem Speicher, wenn es char* ist. Wenn es int* ist, ist das Ergebnis 4.

Es gibt keine Möglichkeit, die Gesamtzuweisung herauszufinden.

1
sarin

Quuxplusone hat geschrieben: "Das Schreiben einer Funktion, die auf einem dieser Void funktionieren kann, ist unmöglich, es sei denn, Sie können aus dem Zeigerwert irgendwie erkennen, von welchen Heapspeichern er stammt." Bestimmen Sie die Größe des dynamisch zugewiesenen Speichers in C "

In Windows gibt _msize tatsächlich die zugewiesene Speichergröße aus dem Wert des Zeigers an. Wenn an der Adresse kein reservierter Speicher vorhanden ist, wird ein Fehler ausgegeben.

int main()
{
    char* ptr1 = NULL, * ptr2 = NULL;
    size_t bsz;    
    ptr1 = (char*)malloc(10);
    ptr2 = ptr1;
    bsz = _msize(ptr2);
    ptr1++;
    //bsz = _msize(ptr1);   /* error */
    free(ptr2);

    return 0;
}

Vielen Dank für die #define-Sammlung. Hier ist die Makro-Version.

#define MALLOC(bsz) malloc(bsz)
#define FREE(ptr) do { free(ptr); ptr = NULL; } while(0)
#ifdef __linux__
#include <malloc.h>
#define MSIZE(ptr) malloc_usable_size((void*)ptr)
#Elif defined __Apple__
#include <malloc/malloc.h>
#define MSIZE(ptr) malloc_size(const void *ptr)
#Elif defined _WIN32
#include <malloc.h>
#define MSIZE(ptr) _msize(ptr)
#else
#error "unknown system"
#endif
0
Thomas_M