Question

J'ai attribué un "tableau". de mystruct de taille n comme ceci:

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

Plus tard, je n'ai accès qu'à p et je n'ai plus n . Existe-t-il un moyen de déterminer la longueur du tableau à partir du pointeur p ?

Je suppose que cela doit être possible, car free (p) ne fait que cela. Je sais que malloc () enregistre la quantité de mémoire allouée et c’est pourquoi il connaît la longueur; peut-être y a-t-il un moyen d'interroger cette information? Quelque chose comme ...

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

Je sais que je devrais retravailler le code afin de connaître n , mais je préférerais que ce ne soit pas si possible. Des idées?

Était-ce utile?

La solution

Non, il n'y a aucun moyen d'obtenir ces informations sans dépendre fortement des détails d'implémentation de malloc . En particulier, malloc peut allouer plus d'octets que vous n'en demandez (par exemple, pour améliorer l'efficacité d'une architecture de mémoire particulière). Il serait bien préférable de modifier votre code afin de garder une trace explicite de n . L’alternative est au moins autant de refonte et une approche beaucoup plus dangereuse (étant donné qu’elle est non standard, abuse de la sémantique des pointeurs et sera un cauchemar de maintenance pour ceux qui viendront après vous): la longueur n à l'adresse malloc'd, suivie du tableau. L'allocation serait alors:

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

n est maintenant stocké dans * ((unsigned long int *) p) et le début de votre tableau est maintenant

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

Modifier: Il suffit de se faire l'avocat du diable ... Je sais que ces "solutions" Tous nécessitent une refonte, mais jouons-le. Bien sûr, la solution présentée ci-dessus n’est qu’une implémentation simpliste d’une structure (bien emballée). Vous pourriez aussi bien définir:

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

et transmettez les arrInfo plutôt que les pointeurs bruts.

Maintenant, nous cuisinons. Mais tant que vous modifiez la conception, pourquoi vous arrêter ici? Ce que vous voulez vraiment, c'est un type de données abstrait (ADT). Tout texte d'introduction pour une classe d'algorithmes et de structures de données le ferait. Un ADT définit l'interface publique d'un type de données mais masque l'implémentation de ce type de données. Ainsi, publiquement, un TDA pour un tableau pourrait ressembler à

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

En d’autres termes, un ADT est une forme d’encapsulation de données et de comportements ... c’est-à-dire qu’il est aussi proche que possible de la programmation orientée objet utilisant un simple C. À moins que vous ne soyez bloqué sur une plate-forme qui n’a pas de compilateur C ++, vous pouvez aussi bien vous contenter d’un STL std :: vector .

Là, nous avons pris une question simple à propos de C et nous sommes retrouvés en C ++. Dieu nous aide tous.

Autres conseils

suivre vous-même la taille du tableau; free utilise la chaîne malloc pour libérer le bloc alloué, qui n'a pas nécessairement la même taille que le tableau que vous avez demandé

.

Juste pour confirmer les réponses précédentes: il n’ya aucun moyen de savoir, simplement en étudiant un pointeur, combien de mémoire a été allouée par un malloc qui a renvoyé ce pointeur.

Et si cela fonctionnait?

Un exemple de pourquoi cela n’est pas possible. Imaginons le code avec une fonction hypothétique appelée get_size (void *) qui renvoie la mémoire allouée pour un pointeur:

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) ;
}

Pourquoi même si cela fonctionnait, cela ne fonctionnerait quand même pas?

Mais le problème de cette approche est qu’en C, vous pouvez jouer avec l’arithmétique par pointeur. Réécrivons doSomethingElse ():

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

Comment get_size est censé fonctionner, car vous avez envoyé à la fonction un pointeur valide, mais pas celui renvoyé par malloc. Et même si get_size rencontrait tous les problèmes pour trouver la taille (de manière inefficace), il renverrait, dans ce cas, une valeur qui serait fausse dans votre contexte.

Conclusion

Il y a toujours moyen d'éviter ce problème, et en C, vous pouvez toujours écrire votre propre allocateur, mais là encore, il est peut-être trop gênant de vous rappeler le peu de mémoire allouée.

Certains compilateurs fournissent msize () ou des fonctions similaires (_msize (), etc.), qui vous permettent de faire exactement cela

Puis-je vous recommander un moyen terrible de le faire?

Allouez tous vos tableaux comme suit:

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

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

Ensuite, vous pouvez toujours convertir vos tableaux en int * et accéder à l'élément -1st.

Veillez à libérer ce pointeur et non au pointeur de tableau lui-même!

De plus, cela causera probablement de terribles insectes qui vous laisseront vous arracher les cheveux. Peut-être que vous pouvez envelopper les fonctions d’allocation dans des appels d’API ou quelque chose de ce genre.

malloc retournera un bloc de mémoire au moins aussi gros que vous avez demandé, mais peut-être plus gros. Ainsi, même si vous pouviez interroger la taille du bloc, cela ne vous donnerait pas de manière fiable la taille de votre tableau. Il vous suffira donc de modifier votre code pour le suivre vous-même.

Pour un tableau de pointeurs, vous pouvez utiliser un tableau terminé par NULL. La longueur peut alors être déterminée comme si elle était faite avec des chaînes. Dans votre exemple, vous pouvez peut-être utiliser un attribut de structure pour marquer puis terminer. Bien sûr, cela dépend si un membre ne peut pas être NULL. Alors disons que vous avez un nom d'attribut, qui doit être défini pour chaque structure de votre tableau, vous pouvez ensuite interroger la taille par:


int size;
struct mystruct *cur;

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

size = cur - myarray;

Btw il devrait être calloc (n, sizeof (struct mystruct)) dans votre exemple.

D'autres ont abordé les limites des pointeurs simples et des implémentations stdlib.h de malloc () . Certaines implémentations fournissent des extensions qui renvoient la taille de bloc allouée pouvant être supérieure à la taille demandée.

Si vous devez avoir ce comportement, vous pouvez utiliser ou écrire un allocateur de mémoire spécialisé. Cette opération la plus simple consiste à implémenter un wrapper autour des fonctions stdlib.h . Quelque chose comme:

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 */
...

En réalité, votre question est la suivante: "puis-je connaître la taille d'un bloc de données malloc'd (ou calloc'd)". Et comme d’autres l’ont dit: non, pas de manière standard.

Cependant, certaines implémentations malloc personnalisées le font, par exemple, http://dmalloc.com/

Je ne suis au courant d'aucun moyen, mais j'imagine que cela réglerait le problème du bidouillage interne du malloc, ce qui est généralement une très, très mauvaise idée.

Pourquoi ne pouvez-vous pas stocker la taille de mémoire allouée?

EDIT: Si vous savez que vous devez retravailler le code pour savoir n, eh bien, faites-le. Oui, il serait peut-être facile et rapide d’essayer de sonder malloc, mais être sûr de n minimiserait la confusion et renforcerait la conception.

L’une des raisons pour lesquelles vous ne pouvez pas demander à la bibliothèque malloc quelle est la taille d’un bloc est que l’allocateur arrondit généralement la taille de votre demande pour respecter certaines exigences de granularité minimale (par exemple, 16 octets). Donc, si vous demandez 5 octets, vous aurez un bloc de taille 16 en retour. Si vous deviez prendre 16 et diviser par 5, vous obtiendriez trois éléments lorsque vous n'en attribuez qu'un. La bibliothèque malloc prendrait un espace supplémentaire pour garder en mémoire le nombre d'octets que vous avez demandé, il est donc préférable que vous en gardiez la trace vous-même.

Ceci est un test de ma routine de tri. Il configure 7 variables pour contenir des valeurs flottantes, puis les assigne à un tableau, qui sert à trouver la valeur maximale.

La magie réside dans l'appel à myMax:

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

Et c'était magique, n'est-ce pas?

myMax attend un pointeur de tableau float (float *). J'utilise donc & amp; arr pour obtenir l'adresse du tableau et le transforme en pointeur float.

myMax attend également le nombre d'éléments du tableau sous la forme d'un entier. J'obtiens cette valeur en utilisant sizeof () pour me donner la taille en octets du tableau et du premier élément du tableau, puis divise le nombre total d'octets par le nombre d'octets dans chaque élément. (Nous ne devrions pas deviner ou coder en dur la taille d’un int parce qu’il s’agit de 2 octets sur un système et de 4 sur certains comme mon Mac OS X, et pourrait être autre chose sur d'autres).

REMARQUE: tout cela est important lorsque vos données peuvent contenir un nombre d'échantillons variable.

Voici le code de test:

#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;
}

Dans la uClibc , il existe une macro MALLOC_SIZE dans 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))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top