Est-ce que “malloc (sizeof (struct a *))” et “malloc (sizeof (struct a))” sont les mêmes?

Cette question est la continuation de l’ appel de Malloc qui se bloque, mais fonctionne ailleurs

J’ai essayé le programme suivant et je l’ai trouvé en train de fonctionner (c’est-à-dire de ne pas tomber en panne – et cela a également été mentionné dans le lien mentionné ci-dessus). Je peux être chanceux de le faire fonctionner mais je cherche une explication raisonnable des experts de SO afin de savoir pourquoi cela fonctionne?!

Voici quelques notions de base sur l’allocation de memory aide de structures et de pointers malloc()

  • malloc(sizeof(struct a) * n) alloue n nombre d’éléments de type struct a . Et, cet emplacement de mémoire peut être stocké et accédé en utilisant un pointer-to-type-"struct a" . Fondamentalement, une struct a * .
  • malloc(sizeof(struct a *) * n) alloue n nombre d’éléments de type struct a * . Chaque élément peut ensuite pointer sur des éléments de type struct a . En gros, malloc(sizeof(struct a *) * n) alloue un array(n-elements)-of-pointers-to-type-"struct a" . De plus, l’emplacement de mémoire alloué peut être enregistré et utilisé à l’aide d’un pointer-to-(pointer-to-"struct a") . Fondamentalement, une struct a ** .

Donc, quand on crée un array(n-elements)-of-pointers-to-type-"struct a" , est-ce

  1. valide d’assigner cela à struct a * au lieu de struct a ** ?
  2. valide pour accéder / dé-référencer le array(n-elements)-of-pointers-to-type-"struct a" alloué array(n-elements)-of-pointers-to-type-"struct a" utilisant un pointer-to-"struct a" ?

 data * array = NULL; if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) { printf("unable to allocate memory \n"); return -1; } 

L’extrait de code est le suivant:

 #include  #include  int main(void) { typedef struct { int value1; int value2; }data; int n = 1000; int i; int val=0; data * array = NULL; if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) { printf("unable to allocate memory \n"); return -1; } printf("allocation successful\n"); for (i=0 ; i<n ; i++) { array[i].value1 = val++; array[i].value2 = val++; } for (i=0 ; i<n ; i++) { printf("%3d %3d %3d\n", i, array[i].value1, array[i].value2); } free(array); printf("freeing successful\n"); return 0; } 

EDIT: OK dis si je fais ce qui suit par erreur

 data * array = NULL; if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) { 

Existe-t-il un moyen de capturer (au moment de la compilation en utilisant n’importe quel drapeau GCC ) ce type de faute de frappe de programmation non intentionnelle qui pourrait fonctionner de temps en temps et exploser à tout moment! J’ai compilé ceci en utilisant -Wall et n’ai trouvé aucun avertissement!

Il semble y avoir un malentendu fondamental.

malloc (sizeof (struct a) * n) alloue n nombre d’éléments de type struct a.

Non, c’est ce que l’on utilise habituellement après un tel appel. malloc(size) alloue une région mémoire d’octets de size . Ce que vous faites avec cette région est entièrement à vous. La seule chose qui compte est de ne pas dépasser les limites de la mémoire allouée. En supposant que 4 octets float et int et 8 octets double , après un malloc(100*sizeof(float)); réussi malloc(100*sizeof(float)); , vous pouvez utiliser les 120 premiers des 400 octets sous forme de tableau de 15 double s, les 120 suivants sous forme de tableau de 30 float s, puis placer un tableau de 20 caractères juste derrière celui-ci et remplir les 140 octets restants avec 35 int s si vous le souhaitez. C’est un comportement défini parfaitement inoffensif.

malloc renvoie un void* , qui peut être implicitement converti en un pointeur de tout type, donc

 some_type **array = malloc(100 * sizeof(data *)); // intentionally unrelated types 

est parfaitement correct, il se peut que ce ne soit tout simplement pas la quantité de mémoire que vous souhaitiez. Dans ce cas, cela est très probable, car les pointeurs ont généralement la même taille, indépendamment de ce qu’ils indiquent.

Plus susceptible de vous donner la mauvaise quantité de mémoire est

 data *array = malloc(n * sizeof(data*)); 

comme vous l’avez eu. Si vous utilisez la mémoire allouée comme un tableau de n éléments de type data , il existe trois possibilités

  1. sizeof(data) < sizeof(data*) . Ensuite, votre seul problème est que vous perdez de l'espace.
  2. sizeof(data) == sizeof(data*) . Tout va bien, pas d'espace perdu, comme si vous n'aviez aucune faute de frappe.
  3. sizeof(data) > sizeof(data*) . Ensuite, vous aurez access à la mémoire à laquelle vous n'auriez pas dû accéder lorsque vous touchez des éléments de tableau ultérieurs, ce qui constitue un comportement indéfini. En fonction de différentes choses, cela pourrait fonctionner systématiquement comme si votre code était correct, planter immédiatement avec une erreur de segmentation ou quoi que ce soit entre les deux (techniquement, il pourrait se comporter de manière à ne pas pouvoir être placé de manière significative entre les deux, mais ce serait inhabituel).

Si vous le faites intentionnellement, sachant que le point 1. ou 2. s'applique, c'est une mauvaise pratique, mais pas une erreur. Si vous le faites involontairement, il s’agit d’une erreur, quel que soit le point concerné: inoffensive mais difficile à trouver tant que vous êtes 1. ou 2. s’applique, préjudiciable mais normalement plus facile à détecter en cas de 3.

Dans vos exemples. data étaient 4 resp. 8 octets (probablement), qui sur un système 64 bits les met en 1. resp. 2. avec une probabilité élevée, sur un système 32 bits en 2 resp. 3

Le moyen recommandé d'éviter de telles erreurs est de:

 type *pointer = malloc(num_elems * sizeof(*pointer)); 

Non.

sizeof(struct a*) est la taille d’un pointeur .
sizeof(struct a) est la taille de la structure entière.

Ce array = (data *)malloc(sizeof(data *) * n) alloue un sizeof(data*) ( pointeur ) à struct data , si vous voulez le faire, vous avez besoin que votre array soit un data** array .

Dans votre cas, vous souhaitez que votre pointeur pointe vers sizeof(data) , une structure en mémoire, pas vers un autre pointeur. Cela nécessiterait une data** (pointeur à pointeur).

est-il valide d’affecter cela à struct un * au lieu de struct un **?

Bien, techniquement parlant, il est valide d’atsortingbuer comme ça, mais il est faux (UB) de déréférencer ce pointeur. Tu ne veux pas faire ça.

valide pour accéder / dé-référencer le tableau alloué (n-éléments) -de-pointeurs-à-type- “struct a” en utilisant un pointeur sur “struct a”?

Non, comportement non défini.