web-dev-qa-db-fra.com

Trouver le décalage d'un élément de structure en c

struct a
{
    struct b
    {
        int i;
        float j;
    }x;
    struct c
    {
        int k;  
        float l;
    }y;
}z;

Quelqu'un peut-il m'expliquer comment trouver le décalage de int k afin que nous puissions trouver l'adresse de int i?

23
MSB

Utilisez offsetof() pour trouver l'offset depuis le début de z ou depuis le début de x.

offsetof() - décalage d'un membre de la structure

SYNOPSIS

   #include <stddef.h>

   size_t offsetof(type, member);

offsetof() renvoie le décalage du membre de champ depuis le début du type de structure.

EXEMPLE

   #include <stddef.h>
   #include <stdio.h>
   #include <stdlib.h>

   int
   main(void)
   {
       struct s {
           int i;
           char c;
           double d;
           char a[];
       };

       /* Output is compiler dependent */

       printf("offsets: i=%ld; c=%ld; d=%ld a=%ld\n",
               (long) offsetof(struct s, i),
               (long) offsetof(struct s, c),
               (long) offsetof(struct s, d),
               (long) offsetof(struct s, a));
       printf("sizeof(struct s)=%ld\n", (long) sizeof(struct s));

       exit(EXIT_SUCCESS);
   }

Vous obtiendrez la sortie suivante sur un Linux, si vous compilez avec GCC:

       offsets: i=0; c=4; d=8 a=16
       sizeof(struct s)=16
36
Gangadhar
struct a foo;
printf("offset of k is %d\n", (char *)&foo.y.k - (char *)&foo);    
printf("offset of i is %d\n", (char *)&foo.x.i - (char *)&foo);

foo.x.i Fait référence au champ i dans la structure x dans la structure foo. &foo.x.i Vous donne l'adresse du champ foo.x.i. De même, &foo.y.k Vous donne l'adresse de foo.y.k; &foo Vous donne l'adresse de la structure foo.

La soustraction de l'adresse de foo de l'adresse de foo.x.i Vous donne le décalage de foo à foo.x.i.

Comme le dit Gangadhar, vous pouvez utiliser la macro offsetof() plutôt que l'arithmétique du pointeur que j'ai donnée. Mais il est bon de comprendre d'abord l'arithmétique du pointeur.

7
Charlie Burns

Cela fait 3 ans que la question a été posée, j'ajoute ma réponse par souci d'exhaustivité.

La manière hacky d'obtenir le décalage d'un membre struct va comme ceci

printf("%p\n", (void*)(&((struct s *)NULL)->i));

Cela n'a pas l'air joli, je ne peux penser à rien en C pur (qui peut vous obtenir le décalage du membre, sans rien savoir de la structure. Je crois que la macro offsetof est définie dans ce mode.

Pour référence, cette technique est utilisée dans le noyau linux, consultez le container_of macro:

http://lxr.free-electrons.com/source/scripts/kconfig/list.h#L18

Une explication plus élaborée peut être trouvée dans cet article:

http://radek.io/2012/11/10/magical-container_of-macro/

7
silen

Comme déjà suggéré, vous devez utiliser la macro offsetof() de <stddef.h>, ce qui donne le décalage en tant que size_t valeur.

Par exemple:

#include <stddef.h>
#include <stdio.h>
#include "struct_a.h"  /* Header defining the structure in the question */

int main(void)
{
    size_t off_k_y = offsetof(struct c, k);
    size_t off_k_z = offsetof(struct a, y.k);
    size_t off_i_x = offsetof(struct b, i);
    size_t off_i_z = offsetof(struct a, x.i);

    printf("k = %zu %zu; i = %zu %zu\n", off_k_y, off_k_z, off_i_x, off_i_z);
    return 0;
}

Exemple de sortie:

k = 0 8; i = 0 0
5
Jonathan Leffler

Voici une solution générique:

#if defined(__GNUC__) && defined(__GNUC_MINOR__)
#  define GNUC_PREREQ(minMajor, minMinor) \
         ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((minMajor) << 16) + (minMinor))
#else
#  define GNUC_PREREQ 0
#endif

#if GNUC_PREREQ(4, 0)
#  define OFFSETOF(type, member) ((int)__builtin_offsetof(type, member))
#else
#  define OFFSETOF(type, member) ((int)(intptr_t)&(((type *)(void*)0)->member) )
#endif
0
clearlight