Frage

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

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

Später habe ich nur noch Zugriff auf p, und nicht mehr n.Gibt es eine Möglichkeit, die Länge des Arrays anhand des Zeigers zu bestimmen? p?

Ich denke es muss möglich sein, da free(p) macht genau das.Ich weiß malloc() verfolgt, wie viel Speicher es zugewiesen hat, und kennt daher die Länge;Gibt es vielleicht eine Möglichkeit, diese Informationen abzufragen?Etwas wie...

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

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

War es hilfreich?

Lösung

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

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

n ist jetzt unter 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 eine Neugestaltung erfordern, aber lassen Sie es uns ausprobieren.Natürlich ist die oben vorgestellte Lösung nur eine hackige Implementierung einer (gut gepackten) Struktur.Sie könnten genauso gut Folgendes definieren:

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

und herumgehen arrInfos statt roher Zeiger.

Jetzt kochen wir.Aber warum sollten Sie hier aufhören, solange Sie mit der Neugestaltung beschäftigt sind?Was Sie wirklich wollen, ist ein abstrakter Datentyp (ADT).Jeder Einführungstext für eine Klasse über Algorithmen und Datenstrukturen würde dies 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.Sofern Sie nicht auf einer Plattform festsitzen, die keinen C++-Compiler hat, können Sie genauso gut gleich aufs Ganze gehen und einfach eine STL verwenden std::vector.

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

Andere Tipps

Behalten Sie die Array -Größe selbst im Auge. Free verwendet die Malloc -Kette, um die zu befreien Block Das wurde zugewiesen, was nicht unbedingt die gleiche Größe wie das von Ihnen angeforderte Array hat

Nur um die vorherigen Antworten zu bestätigen: Es gibt keine Möglichkeit zu wissen, nur wenn ein Zeiger studiert, wie viel Gedächtnis von einem Malloc zugewiesen wurde, der diesen Zeiger zurückgab.

Was ist, wenn es funktioniert hat?

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, auch wenn es funktionierte, würde es sowieso nicht funktionieren?

Das Problem dieses Ansatzes ist jedoch, dass Sie in C mit Zeigerarithmetika spielen können. Schreiben wir dosomethingelse () neu:

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 der von Malloc zurückgegebene. Und selbst wenn Get_Size alle Schwierigkeiten durchgemacht hat, die Größe (dh auf ineffiziente Weise) zu finden, würde dies in diesem Fall ein Wert zurückkehren, 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 es ist vielleicht zu viel Mühe, wenn Sie sich nur daran erinnern, wie viel Gedächtnis zugewiesen wurde.

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

Darf ich einen schrecklichen Weg empfehlen, dies zu tun?

Weisen Sie alle Ihre Arrays wie folgt aus:

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 aufgeben int * und greifen Sie auf das -1st -Element zu.

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

Dies wird wahrscheinlich schreckliche Fehler verursachen, die Sie die Haare ausreißen lassen. Vielleicht können Sie die Alloc -Funcs in API -Anrufen oder so umwickeln.

Malloc wird einen Speicherblock mindestens so groß wie Sie angefordert, aber möglicherweise größer. Selbst wenn Sie die Blockgröße abfragen könnten, würde dies Ihnen nicht zuverlässig Ihre Array -Größe geben. Sie müssen also nur Ihren Code ändern, um ihn selbst zu verfolgen.

Für eine Reihe von Zeigern können Sie ein null-terminiertes Array verwenden. Die Länge kann dann so bestimmen, als würde sie mit Saiten fertiggestellt. In Ihrem Beispiel können Sie möglicherweise ein Strukturattribut verwenden, um zu markieren und dann zu enden. 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, die Sie dann abfragen können, nach:


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.

Andere haben die Grenzen der einfachen C -Zeiger und die diskutiert stdlib.h Implementierungen von malloc(). Einige Implementierungen bieten Erweiterungen, die die zurückgeben zugewiesen Blockgröße, die größer sein kann als die angeforderte Größe.

Wenn du muss Haben Sie dieses Verhalten, das Sie verwenden oder einen speziellen Speicher Allocator schreiben können. Diese einfachste Sache, die zu tun ist stdlib.h Funktionen. Etwas wie:

void* my_malloc(size_t s);     /* Calls malloc(s), and if successful stores 
                                  (p,s) in a list of handled blocks */
void my_free(void* p);         /* Removes list entry and calls free(p) */
size_t my_block_size(void* p); /* Looks up p, and returns the stored size */
...

Wirklich Ihre Frage lautet: "Kann ich die Größe eines Malloc -Datenblocks (oder CALLOC'D) herausfinden? Und wie andere gesagt haben: Nein, nicht auf Standard.

Es gibt jedoch benutzerdefinierte Malloc -Implementierungen, die dies tun - zum Beispiel http://dmalloc.com/

Mir ist kein Weg bewusst, aber ich würde mir vorstellen, dass es sich darum kümmert, in Mallocs Interna herumzugehen, was im Allgemeinen eine sehr, sehr schlechte Idee ist.

Warum können Sie die Größe des Speichers nicht speichern, die Sie zugewiesen haben?

Bearbeiten: Wenn Sie wissen, dass Sie den Code überarbeiten sollten, damit Sie n wissen, tun Sie es. Ja, es könnte schnell und einfach sein, Malloc zu befragen, aber wenn man n mit Sicherheit Verwirrung minimiert und das Design stärkt.

Einer der Gründe, warum Sie die Malloc -Bibliothek nicht fragen können, wie groß ein Block ist, ist, dass der Allocator normalerweise die Größe Ihrer Anfrage rundet, um einige minimale Granularitätsanforderungen zu erfüllen (z. B. 16 Bytes). Wenn Sie also nach 5 Bytes fragen, erhalten Sie einen Block der Größe 16 zurück. Wenn Sie 16 nehmen und sich um 5 teilen würden, erhalten Sie drei Elemente, wenn Sie wirklich nur einen zuweisen. Die Malloc -Bibliothek würde zusätzlichen Platz benötigen, um zu verfolgen, wie viele Bytes Sie überhaupt gefragt haben. Es ist also am besten, dass Sie dies selbst im Auge behalten.

Dies ist ein Test meiner Art Routine. Es richtet 7 Variablen zum Halten von Float -Werten ein und weist sie dann einem Array zu, das verwendet wird, um den maximalen Wert zu finden.

Die Magie ist im Anruf an Mymax:

float mmax = mymax ((float *) & arr, (int) sizeof (arr)/sizeof (arr [0]));

Und das war magisch, nicht wahr?

MYMAX erwartet einen Float -Array -Zeiger (Float *), also benutze ich und arr, um die Adresse des Arrays zu erhalten und ihn als Float -Zeiger zu werfen.

MYMAX erwartet auch die Anzahl der Elemente im Array als int. Ich erhalte diesen Wert, indem ich sizeof () verwende, um mir Bytegrößen des Arrays und des ersten Elements des Arrays zu geben, und dann die Gesamtbytes durch die Anzahl der Bytes in jedem Element zu teilen. (Wir sollten die Größe eines INT nicht erraten oder codieren, da es 2 Bytes auf einem System und 4 auf einigen wie meinen OS X -Mac beträgt und für andere etwas anderes sein könnte.)

Hinweis: All dies ist wichtig, wenn Ihre Daten möglicherweise eine unterschiedliche Anzahl von Proben haben.

Hier ist der Testcode:

#include <stdio.h>

float a, b, c, d, e, f, g;

float myMax(float *apa,int soa){
 int i;
 float max = apa[0];
 for(i=0; i< soa; i++){
  if (apa[i]>max){max=apa[i];}
  printf("on i=%d val is %0.2f max is %0.2f, soa=%d\n",i,apa[i],max,soa);
 }
 return max;
}

int main(void)
{
 a = 2.0;
 b = 1.0;
 c = 4.0;
 d = 3.0;
 e = 7.0;
 f = 9.0;
 g = 5.0;
 float arr[] = {a,b,c,d,e,f,g};

 float mmax = myMax((float *)&arr,(int) sizeof(arr)/sizeof(arr[0]));
 printf("mmax = %0.2f\n",mmax);

 return 0;
}

Im UCLIBC, da ist ein MALLOC_SIZE Makro in malloc.h:

/* The size of a malloc allocation is stored in a size_t word
   MALLOC_HEADER_SIZE bytes prior to the start address of the allocation:

     +--------+---------+-------------------+
     | SIZE   |(unused) | allocation  ...   |
     +--------+---------+-------------------+
     ^ BASE             ^ ADDR
     ^ ADDR - MALLOC_HEADER_SIZE
*/

/* The amount of extra space used by the malloc header.  */
#define MALLOC_HEADER_SIZE          \
  (MALLOC_ALIGNMENT < sizeof (size_t)       \
   ? sizeof (size_t)                \
   : MALLOC_ALIGNMENT)

/* Set up the malloc header, and return the user address of a malloc block. */
#define MALLOC_SETUP(base, size)  \
  (MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE))
/* Set the size of a malloc allocation, given the base address.  */
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size))

/* Return base-address of a malloc allocation, given the user address.  */
#define MALLOC_BASE(addr)   ((void *)((char *)addr - MALLOC_HEADER_SIZE))
/* Return the size of a malloc allocation, given the user address. */
#define MALLOC_SIZE(addr)   (*(size_t *)MALLOC_BASE(addr))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top