36 votes

C pointeur arithmétique sans objet de structure

Je pense qu'il n'est pas possible en C, mais demander à vérifier. Est-il possible de faire de l'arithmétique entre les membres de structure sans réel variable de ce type? Par exemple:

typedef struct _s1
{
  int a;
  int b;
  int c;
} T1;

Je veux voir le décalage de "c" membre par rapport à la structure début. Il est facile si j'ai une variable:

T1 str;

int dist = (int)&str.c - (int)&str;

Mais ma structure est trop gros et il n'a pas de membre dans la mémoire RAM (seulement dans l'EEPROM). Et je veux faire de l'adresse de calculs, mais de ne pas définir la RAM membre. Je peux faire le travail avec pointeur de structure au lieu de la variable de structure (il vous en coûtera seulement 4 octets), mais le cas est intéressant pour moi.

57voto

pmr Points 30450

À l'aide de offsetof vous pouvez effectuer des calculs entre les membres, sans avoir à obtenir une variable de ce type. Tous vous avez besoin est le type lui-même et le nom du membre.

Une remarque pourquoi plaine calculs sont susceptibles de ne pas travailler: l'alignement des données. Vous ne connaissez pas la quantité de remplissage de votre compilateur va se jeter à votre structure et cela peut conduire à de très subtiles erreurs ou de faire semblant de corriger les calculs de mal, si vous modifiez la structure.

25voto

Elias Van Ootegem Points 29404

Tout d'abord, vous êtes à la coulée de l'individu adresses ints. Je ne pense pas que ce soit une bonne idée. un int est garanti pour au moins 2 octets ou plus en taille, une adresse/pointeur est généralement de 4 ou 8 octets. Casting ces int n'a tout simplement pas de s'asseoir droit. Si je devais jeter à rien, je serais probablement utiliser unsigned long pour les systèmes 32 bit, et unsigned long long 64 bits. Je voudrais utiliser ce dernier pour être sûr.
Cela dit, je ne jette pas les adresses de tous, et il suffit d'utiliser l' offsetof macro.

Ajouter #include <stddef.h> à votre fichier, et l'utilisation de l' offsetof macro, qui jette la valeur de décalage d' size_t type, pas un int, vous l'esprit. Il fonctionne simplement par l'utilisation de 0 que l'adresse mémoire de la structure, et ensuite renvoie l'adresse de la donnée membre, donnant le décalage réel:

size_t offset = offsetoff(T1, c);
                    //struct, member

Comme Zack a souligné dans son commentaire, la prochaine partie de la réponse est un peu hors de propos, mais pour les liens qu'il contient, et à des fins d'exhaustivité, je vais juste le laisser ici - que offsetof est exigée pour toutes les implémentations:

Définir la macro-vous, si vous n'avez pas stddef.h pour une raison quelconque, comme suit:

#ifndef offsetof
#define offsetof(s, m) ((size_t)&(((s *) 0)->m))
#endif

Que devrait faire l'affaire, mais méfiez-vous: si votre compilateur n'a pas vous fournir avec un offsetof-contenant en-tête, qui peut être parce qu'il peut invoquer un comportement indéterminé.
J'ai pris l' offsetof définition ci-dessus de la stddef.h source que j'ai trouvé ici, de sorte que vous pouvez il suffit de télécharger ce fichier, et de l'utiliser, au lieu de définir la macro dans votre propre fichier, mais gardez à l'esprit que l' offsetof vous utilisez n'est pas fiable à 100%.

Note:
Comme pmr signalé hors de moi, et comme je l'ai expliqué ensuite (ou au moins, essayé de l'expliquer), il s'avère offsetof n'est pas infaillible. Toutefois, si vous utilisez gcc, il met en œuvre ses propres __builtin_offsetof, donc:

#ifndef offsetof
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
#endif

doit être sûr à utiliser. GCC devrait venir avec un stddef.h de toute façon, de sorte que vous pouvez supprimer l' #define de toute façon. Et, comme l'explique l'un des sites les réponses, GCC préféreront toujours l'utilisation de son propre, natif offsetof à la vôtre.

De toute façon, c'est assez pour le moment, plus d'infos sur Google, mais voici quelques liens:

16voto

fede1024 Points 1966

Vous pouvez utiliser offsetof définie en stddef.h.

Je sais que cela va au-delà de ce que vous avez demandé, mais il y a une utilisation intéressante de l' offsetof utilisé par exemple dans le code du noyau de linux, l' container_of macro. container_of pouvons vous renvoyer l'adresse de la structure mère, un pointeur vers un de ses membres:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

Que vous pouvez utiliser, par exemple:

// pointer_to_c points to a member of the struct, c in this case
// and you want to get the address of the container
T1 *container;
container = container_of(pointer_to_c, T1, c);

12voto

this Points 3713

``a une macro appelée offsetof qui donne le décalage d'un membre dès le début d'une structure.

De cette façon, vous n'avez pas à déclarer réellement des variables structurelles.

10voto

Alter Mann Points 10564

Jetez un oeil à l'alignement de la structure de données

C'est exact:

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X