J'ai un problème pour accéder au pointeur vers une structure (tableau passé à la fonction) qui a un tableau de taille variable.
char name[20]; char value[20]; }param_t; typedef struct object { char name[20]; int no_of_params; param_t params[]; }; int main() { int no_of_objs = 3, no_of_params = 5; object_t *objs = malloc(no_of_objs * (sizeof(object_t) + (no_of_params * sizeof(param_t)) ); //... //... objs++; //Increment to point to the next item <-- Problem: ignores params[] size // blah // blah // blah
J'ai alloué de la mémoire pour avoir 3 object_t avec chaque object_t stockant 5 param_t.
L'allocation de mémoire est bonne et je pourrais définir des valeurs pour les membres. objs [0] est parfaitement bien. Maintenant, si j'incrémente les objs (objs ++) pour pointer vers l'objet suivant, il pointe en fait vers l'adresse de paramètre de l'objet précédent. Il ignore complètement les paramètres []. Maintenant, si je fixe des valeurs pour le deuxième * objs, il écrase en fait les paramètres [] des * objs précédents.
Ma question est: est-ce un bogue dans le compilateur C? Sinon, comment parcourir les objets?
3 Réponses :
Ce n'est pas un bogue dans le compilateur C, juste un inconvénient de l'utilisation de tableaux de longueur variable.
Les compilateurs sont autorisés à traiter le membre du tableau de longueur variable de la struct
comme ne contribuant pas à la sizeof
.
Cela rompt efficacement l'arithmétique du pointeur, et cela signifie que vous ne pouvez pas facilement "parcourir les objets", comme vous le dites.
C'est la vie, j'ai peur. Une raison suffisante pour supprimer les tableaux de longueur variable comme exigence obligatoire de C11.
Je conviens que c'est toute une tâche. Je pense qu'il faut éviter les tableaux de longueur variable à moins que cela ne soit absolument nécessaire.
Selon la réponse de Bathsheba, vous pourriez mieux l'implémenter comme un tableau de pointeurs vers les objets:
int no_of_objs = 3, no_of_params = 5; object_t **objs = malloc(no_of_objs * sizeof(object_t *); for (int i=0; i<no_of_objs; i++) objs[i]= malloc ((sizeof(object_t) + (no_of_params * sizeof(param_t)) );
Si vous avez vraiment besoin d'allouer cette mémoire en un seul bloc, vous devez déterminer une taille d'élément correctement alignée:
object_t *indexed_object_t(object_t *base, size_t nparams, int idx) { return (object_t *)((char *)base + (intmax_t)object_t_size(nparams) * idx); }
Ici, nparams
est le nombre des éléments param_t
alloués pour le membre du tableau flexible params
de object_t
.
Pour parcourir un bloc de mémoire contenant une collection de object_t
s avec params
membres de tableau flexibles, vous avez besoin d'un moyen de trouver la position du prochain object_t
:
XXX
Ce qui précède peut être utilisé même si les object_t
ont des tailles différentes (y compris leurs paramètres
membres de tableau flexible) tant que les tailles de chaque object_t
individuel est connu et ne change pas après l'allocation (ou du moins, pas sans code pour réallouer la mémoire et mélanger autour du contenu pour agrandir ou réduire la taille d'un object_t
Si tous les object_t
s dans la collection s'ils ont la même taille (y compris la taille fixe de leur membre de tableau flexible params
), vous pouvez obtenir un pointeur vers l'élément ith :
object_t *indexed_object_t(object_t *base, size_t nparams, size_t idx) { return (object_t *)((char *)base + object_t_size(nparams) * idx); }
Notez que la fonction ci-dessus n'est pas adaptée à l'indexation à l'envers à partir d'un élément arbitraire. Cela pourrait être résolu en utilisant un type signé pour l'index et en convertissant la valeur de retour de object_t_size
en un type entier signé convenablement large:
object_t *next_object_t(object_t *obj, size_t nparams) { return (object_t *)((char *)obj + object_t_size(nparams)); }
Le typage en char * et l'incrémentation manuelle est une bonne technique. Ça fait l'affaire. Merci. Maintenant, je suis sur le tableau de longueur variable dans le param_t (structures imbriquées avec les deux ayant un tableau de longueur variable) :)
Hormis les problèmes de
sizeof
, il est fort probable que& objs [0] .params [objs [0] .no_of_params]
ne satisfera pas aux exigences d'alignement pour leobject_t
.