Question

J'ai une définition de structure de test comme suit:

struct test{
    int a, b, c;
    bool d, e;
    int f;
    long g, h;
};

Et quelque part, je l'utilise de cette façon:

test* t = new test;   // create the testing struct
int* ptr = (int*) t;
ptr[2] = 15;          // directly manipulate the third word
cout << t->c;         // look if it really affected the third integer

Cela fonctionne correctement sous Windows. Il en imprime 15 comme prévu, mais est-il sûr? Puis-je être vraiment sûr que la variable est sur place dans la mémoire que je souhaite, surtout si de telles structures sont combinées (par exemple, f est sur le cinquième mot de mon compilateur, mais c'est une sixième variable)?

Si ce n'est pas le cas, existe-t-il un autre moyen de manipuler directement les membres de la structure sans avoir réellement une construction struct- > member dans le code?

Était-ce utile?

La solution

On dirait que vous posez deux questions

Est-il prudent de traiter et de tester comme un tableau interne de 3 longueurs?

Il vaut probablement mieux éviter cela. C’est peut-être une action définie dans la norme C ++, mais même si c’est le cas, il est peu probable que tous ceux avec qui vous travaillez comprennent ce que vous faites ici. Je pense que cela n’est pas supporté si vous lisez la norme à cause du potentiel de remplissage des structures, mais je ne suis pas sûr.

Existe-t-il un meilleur moyen d'accéder à un membre sans son nom?

Oui. Essayez d’utiliser la macro / l'opérateur offsetof . Ceci fournira le décalage de mémoire d'un membre particulier dans une structure et vous permettra de positionner correctement un point sur ce membre.

size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);

Une autre solution consisterait simplement à prendre l'adresse de c directement

int* pointerToC = &(someTest->c);

Autres conseils

Non, vous ne pouvez pas être sûr. Le compilateur est libre d'introduire un remplissage entre les membres de la structure.

Pour ajouter à la la réponse de JaredPar , une autre option en C ++ uniquement (non en clair C) consiste à créer un objet pointeur vers membre:

struct test
{
  int a, b, c;
  bool d, e;
  int f;
  long g, h;
};

int main(void)
{
  test t1, t2;

  int test::*p;  // declare p as pointing to an int member of test
  p = &test::c;  // p now points to 'c', but it's not associating with an object
  t1->*p = 3;    // sets t1.c to 3
  t2->*p = 4;    // sets t2.c to 4

  p = &test::f;
  t1->*p = 5;    // sets t1.f to 5
  t2->*p = 6;    // sets t2.f to 6
}

Vous recherchez probablement la macro offsetof . Cela vous donnera le décalage d'octet du membre. Vous pouvez ensuite manipuler le membre à ce décalage. Notez cependant que cette macro est spécifique à l'implémentation. Incluez stddef.h pour que cela fonctionne.

Ce n'est probablement pas sûr et il est illisible à 100%; rendant ainsi ce genre de code inacceptable dans le code de production réel.

utilisez les méthodes set et boost :: bind pour créer un foncteur qui modifiera cette variable.

Hormis les problèmes de remplissage / alignement soulevés par d'autres réponses, votre code enfreint les règles strictes en matière de crénelage, ce qui signifie qu'il risque de ne pas fonctionner correctement pour les versions optimisées (vous ne savez pas comment MSVC procède ainsi, mais GCC -O3 rompre avec ce type de comportement). Essentiellement, du fait que test * t et int * ptr sont de types différents, le compilateur peut supposer qu'ils pointent vers différentes parties de la mémoire et il peut réorganiser les opérations.

Considérez cette modification mineure:

test* t = new test;
int* ptr = (int*) t;

t->c = 13;
ptr[2] = 15;
cout << t->c;

La sortie à la fin peut être 13 ou 15 , en fonction de l'ordre des opérations utilisées par le compilateur.

Selon le paragraphe 9.2.17 de la norme, il est en fait légal de transformer un pointeur en struct en pointeur vers son premier membre, à condition que la structure soit POD :

  

Un pointeur sur un objet POD-struct,   convenablement converti en utilisant un   reinterpret_cast, pointe vers son   membre initial (ou si ce membre est un   champ de bits, puis à l'unité dans laquelle   il réside) et vice versa. [Remarque:   Il pourrait donc être sans nom   remplissage dans un objet POD-struct,   mais pas à son début, comme nécessaire   pour obtenir un alignement approprié. ]

Cependant, la norme ne donne aucune garantie quant à la disposition des structures - même des structures POD - si l'adresse d'un membre ultérieur est supérieure à celle d'un membre précédent, à condition qu'aucun spécificateur d'accès ne soit spécifié (< code> privé: , protégé: ou public: ) entre eux. Ainsi, traiter la partie initiale de votre struct test comme un tableau de 3 entiers constitue un comportement non défini techniquement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top