0
votes

Comment incrémenter le pointeur vers une structure qui a un tableau de longueur variable?

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?


1 commentaires

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 le object_t .


3 Réponses :


1
votes

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.


1 commentaires

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.



0
votes

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


0 commentaires

0
votes

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


1 commentaires

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) :)