Accéder directement aux membres de la structure
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?
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.