Wie kann ich die Größe eines Arrays von einem Zeiger in C erhalten?

Lesezeit: 8 Minuten

Benutzeravatar von Joel
Joel

Ich habe ein “Array” von zugewiesen mystruct von Größe n so was:

if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) {
 /* handle error */
}

Später habe ich nur Zugriff auf pund nicht mehr haben n. Gibt es eine Möglichkeit, die Länge des Arrays zu bestimmen, wenn nur der Zeiger angegeben ist? p?

Ich schätze es muss möglich sein, da free(p) tut genau das. Ich weiss malloc() verfolgt, wie viel Speicher zugewiesen wurde, und kennt daher die Länge; Vielleicht gibt es eine Möglichkeit, diese Informationen abzufragen? Etwas wie…

int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)

Ich weiß, ich sollte nur den Code überarbeiten, damit ich es weiß n, aber ich möchte lieber nicht, wenn möglich. Irgendwelche Ideen?

  • Die Antworten lauteten zwar alle „Mach es richtig“, aber es war eine wirklich gute Frage. Also stimme zu 😉

    – David Arno

    24. Oktober 2008 um 7:25 Uhr

Benutzeravatar von Barry Wark
Barry Wark

Nein, es gibt keine Möglichkeit, diese Informationen zu erhalten, ohne stark von den Implementierungsdetails abhängig zu sein malloc. Im Speziellen, malloc kann mehr Bytes zuweisen, als Sie anfordern (z. B. aus Gründen der Effizienz in einer bestimmten Speicherarchitektur). Es wäre viel besser, Ihren Code so umzugestalten, dass Sie den Überblick behalten n ausdrücklich. Die Alternative ist wenigstens so viel Neugestaltung und ein viel gefährlicherer Ansatz (da er nicht standardisiert ist, die Semantik von Zeigern missbraucht und ein Wartungsalptraum für diejenigen sein wird, die nach Ihnen kommen): Speichern Sie die Längen an der Malloc-Adresse, gefolgt vom Array. Die Zuordnung wäre dann:

void *p = calloc(sizeof(struct mystruct) * n + sizeof(unsigned long int),1));
*((unsigned long int*)p) = n;

n ist jetzt bei gespeichert *((unsigned long int*)p) und der Anfang Ihres Arrays ist jetzt

void *arr = p+sizeof(unsigned long int);

Bearbeiten: Nur um den Advokaten des Teufels zu spielen … Ich weiß, dass diese “Lösungen” alle neu gestaltet werden müssen, aber lassen Sie es uns ausprobieren. Natürlich ist die oben vorgestellte Lösung nur eine hackige Implementierung einer (gut gepackten) Struktur. Du könntest genauso gut definieren:

typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;

und herumreichen arrInfos anstelle von rohen Zeigern.

Jetzt kochen wir. Aber solange Sie umgestalten, warum hier aufhören? Was Sie wirklich wollen, ist ein abstrakter Datentyp (ADT). Jeder einführende Text für eine Klasse über Algorithmen und Datenstrukturen würde es tun. Ein ADT definiert die öffentliche Schnittstelle eines Datentyps, verbirgt jedoch die Implementierung dieses Datentyps. So könnte öffentlich ein ADT für ein Array aussehen

typedef void* arrayInfo;
(arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize);
(void)deleteArrayInfo(arrayInfo);
(unsigned int)arrayLength(arrayInfo);
(void*)arrayPtr(arrayInfo);
...

Mit anderen Worten, ein ADT ist eine Form der Daten- und Verhaltenskapselung … mit anderen Worten, es kommt der objektorientierten Programmierung mit reinem C so nahe wie möglich. Es sei denn, Sie stecken auf einer Plattform fest, auf der dies nicht der Fall ist Wenn Sie einen C++-Compiler haben, können Sie genauso gut aufs Ganze gehen und einfach eine STL verwenden std::vector.

Dort haben wir eine einfache Frage zu C genommen und sind bei C++ gelandet. Gott helfe uns allen.

  • @Joel – Denken Sie jemals daran, wie Sie löschen [] * p schafft es, alle Destruktoren in dem Array aufzurufen, auf das p zeigt – nun, das ist coz new macht dasselbe, was bary vorgeschlagen hat. new speichert die Anzahl der Elemente im Array am Anfang des Arrays und gibt Ihnen den Zeiger über diese erste Stelle hinaus.

    – Rechenleben

    24. Oktober 2008 um 18:04 Uhr

  • @computinglife – nicht unbedingt, ein Zuordner könnte Metadaten leicht in einem anderen Teil des Speichers als den Bits aufbewahren, die er ausgibt, um zu verhindern, dass Pufferüberläufe interne Datenstrukturen beschädigen, oder die Zahl einige Bytes früher platzieren.

    – vergänglich

    28. Oktober 2008 um 19:02 Uhr

  • Tatsächlich platziert der Standardzuordner von glibc die Größe direkt vor dem zurückgegebenen Zeiger, verwendet aber die unteren Bits für Metadaten – daher muss die Zahl maskiert werden, um genau zu sein.

    – vergänglich

    28. Oktober 2008 um 19:03 Uhr

  • Mit a kann man nicht rechnen void *p so wie das.

    – abschalten

    15. April 2016 um 10:21 Uhr

Verfolgen Sie die Array-Größe selbst; free verwendet die Malloc-Kette, um die Block das zugewiesen wurde, das nicht unbedingt die gleiche Größe wie das von Ihnen angeforderte Array haben muss

Nur um die vorherigen Antworten zu bestätigen: Es gibt keine Möglichkeit, einfach durch das Studium eines Zeigers zu wissen, wie viel Speicher von einem Malloc zugewiesen wurde, der diesen Zeiger zurückgab.

Was wäre, wenn es funktionierte?

Ein Beispiel dafür, warum dies nicht möglich ist. Stellen wir uns den Code mit einer hypothetischen Funktion namens get_size(void *) vor, die den für einen Zeiger zugewiesenen Speicher zurückgibt:

typedef struct MyStructTag
{ /* etc. */ } MyStruct ;

void doSomething(MyStruct * p)
{
   /* well... extract the memory allocated? */
   size_t i = get_size(p) ;
   initializeMyStructArray(p, i) ;
}

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   doSomething(s) ;
}

Warum, selbst wenn es funktionierte, würde es trotzdem nicht funktionieren?

Das Problem bei diesem Ansatz ist jedoch, dass Sie in C mit Zeigerarithmetik spielen können. Schreiben wir doSomethingElse() um:

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   MyStruct * s2 = s + 5 ; /* s2 points to the 5th item */
   doSomething(s2) ; /* Oops */
}

Wie get_size funktionieren soll, da Sie der Funktion einen gültigen Zeiger gesendet haben, aber nicht den von malloc zurückgegebenen. Und selbst wenn get_size sich die Mühe machte, die Größe zu finden (dh auf ineffiziente Weise), würde es in diesem Fall einen Wert zurückgeben, der in Ihrem Kontext falsch wäre.

Fazit

Es gibt immer Möglichkeiten, dieses Problem zu vermeiden, und in C können Sie immer Ihren eigenen Allocator schreiben, aber auch hier ist es vielleicht zu viel Mühe, wenn Sie sich nur daran erinnern müssen, wie viel Speicher zugewiesen wurde.

  • Die Tatsache, dass get_size ein Zeiger auf den Anfang eines zugewiesenen Blocks übergeben werden muss, ist kein Hindernis dafür. Übergeben Sie einfach keinen ungültigen Wert. free() hat die gleiche Einschränkung, und die existiert …

    – Steve Jessop

    24. Oktober 2008 um 10:41 Uhr

  • Natürlich, aber free wird normalerweise in diesem Sinne verwendet, zusammen mit dem malloc, das den Speicher zugewiesen hat. get_size würde überall verwendet werden, auch dort, wo der Benutzer nicht wissen soll, wie der Speicher insgesamt zugewiesen wurde (auf dem Stack, über einen Pool usw.).

    – Paercebal

    25. Oktober 2008 um 10:17 Uhr

  • +1 für hervorragende Erklärung. Mein einziges Problem ist: Was wäre, wenn es funktionieren würde und es Grenzen gäbe, was man damit machen könnte? Wie dmkee an anderer Stelle in Kommentaren darauf hinweist, heißt es auf OSX (meiner Plattform). malloc_size(), und es funktioniert genau wie gewünscht. Da ist „das kannst du nicht“ und „du solltest sehr vorsichtig sein, wenn du das tust“ – zwei sehr unterschiedliche Dinge! 🙂

    – Oli

    28. Mai 2013 um 21:04 Uhr


Einige Compiler bieten msize() oder ähnliche Funktionen (_msize() usw.), mit denen Sie genau das tun können

Darf ich einen schrecklichen Weg empfehlen, es zu tun?

Ordnen Sie alle Ihre Arrays wie folgt zu:

void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int));

((int *)blockofMem)[0] = n;
mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);

Dann können Sie Ihre Arrays jederzeit umwandeln int * und greifen Sie auf das -1. Element zu.

Sicher sein zu free dieser Zeiger und nicht der Array-Zeiger selbst!

Außerdem wird dies wahrscheinlich schreckliche Fehler verursachen, die Sie dazu bringen werden, sich die Haare auszureißen. Vielleicht können Sie die Alloc-Funktionen in API-Aufrufe oder so einpacken.

  • Nicht gut für portablen Code, da es nicht funktioniert, wenn mystruct Mitglieder enthält, deren Ausrichtungsanforderung größer als sizeof(int) ist. Offensichtlich kein Problem auf Plattformen, auf denen sizeof(int) ein Vielfaches der größten Ausrichtungsanforderung aller Art ist, aber mit zB -mfaster-structs auf SPARC brechen würde.

    – Steve Jessop

    24. Oktober 2008 um 10:35 Uhr

Benutzeravatar von David Arno
David Arno

malloc gibt einen Speicherblock zurück, der mindestens so groß ist, wie Sie angefordert haben, aber möglicherweise größer. Selbst wenn Sie also die Blockgröße abfragen könnten, würde Ihnen dies nicht zuverlässig Ihre Arraygröße liefern. Sie müssen also nur Ihren Code ändern, um ihn selbst zu verfolgen.

  • Nicht gut für portablen Code, da es nicht funktioniert, wenn mystruct Mitglieder enthält, deren Ausrichtungsanforderung größer als sizeof(int) ist. Offensichtlich kein Problem auf Plattformen, auf denen sizeof(int) ein Vielfaches der größten Ausrichtungsanforderung aller Art ist, aber mit zB -mfaster-structs auf SPARC brechen würde.

    – Steve Jessop

    24. Oktober 2008 um 10:35 Uhr

Benutzeravatar von quinmars
Quinmare

Für ein Array von Zeigern können Sie ein NULL-terminiertes Array verwenden. Die Länge lässt sich dann wie bei Schnüren bestimmen. In Ihrem Beispiel können Sie vielleicht ein Strukturattribut verwenden, um das Ende zu markieren. Das hängt natürlich davon ab, ob es ein Mitglied gibt, das nicht NULL sein kann. Nehmen wir also an, Sie haben einen Attributnamen, der für jede Struktur in Ihrem Array festgelegt werden muss, mit dem Sie dann die Größe abfragen können:


int size;
struct mystruct *cur;

for (cur = myarray; cur->name != NULL; cur++)
    ;

size = cur - myarray;

Übrigens sollte es in Ihrem Beispiel calloc (n, sizeof (struct mystruct)) sein.

1416630cookie-checkWie kann ich die Größe eines Arrays von einem Zeiger in C erhalten?

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

Privacy policy