Quel est l'objectif et le type de retour de l'opérateur __builtin_offsetof?

StackOverflow https://stackoverflow.com/questions/400116

  •  03-07-2019
  •  | 
  •  

Question

Quel est le but de l'opérateur __builtin_offsetof (ou de l'opérateur _FOFF dans Symbian) en C ++?

En plus qu'est-ce qu'il retourne? Aiguille? Nombre d'octets?

Était-ce utile?

La solution

C’est une construction fournie par le compilateur GCC pour implémenter la macro offsetof spécifiée par les standards C et C ++:

GCC - compenser

Il retourne le décalage en octets auquel un membre d'une structure / union POD se trouve.

Exemple:

struct abc1 { int a, b, c; };
union abc2 { int a, b, c; };
struct abc3 { abc3() { } int a, b, c; }; // non-POD
union abc4 { abc4() { } int a, b, c; };  // non-POD

assert(offsetof(abc1, a) == 0); // always, because there's no padding before a.
assert(offsetof(abc1, b) == 4); // here, on my system
assert(offsetof(abc2, a) == offsetof(abc2, b)); // (members overlap)
assert(offsetof(abc3, c) == 8); // undefined behavior. GCC outputs warnings
assert(offsetof(abc4, a) == 0); // undefined behavior. GCC outputs warnings

@Jonathan fournit un bel exemple de l'endroit où vous pouvez l'utiliser. Je me souviens de l'avoir vu utilisé pour implémenter des listes intrusives (listes dont les éléments de données incluent les pointeurs next et prev eux-mêmes), mais je ne me souviens pas où cela a été utile pour l'implémenter, malheureusement.

Autres conseils

Comme le signalent @litb et que @JesperE l’indique, offsetof () fournit un décalage entier en octets (sous forme de size_t ).

Quand pouvez-vous l'utiliser?

Un cas dans lequel cela pourrait être pertinent est une opération pilotée par une table pour lire un nombre énorme de paramètres de configuration divers dans un fichier et en insérer les valeurs dans une structure de données tout aussi énorme. Réduisant énormément à SO si trivial (et ignorant une grande variété de pratiques nécessaires du monde réel, telles que la définition de types de structure dans les en-têtes), je veux dire que certains paramètres peuvent être des entiers et d'autres des chaînes, et que le code peut ressembler légèrement:

#include <stddef.h>

typedef stuct config_info config_info;
struct config_info
{
   int parameter1;
   int parameter2;
   int parameter3;
   char *string1;
   char *string2;
   char *string3;
   int parameter4;
} main_configuration;

typedef struct config_desc config_desc;
static const struct config_desc
{
   char *name;
   enum paramtype { PT_INT, PT_STR } type;
   size_t offset;
   int   min_val;
   int   max_val;
   int   max_len;
} desc_configuration[] =
{
    { "GIZMOTRON_RATING", PT_INT, offsetof(config_info, parameter1), 0, 100, 0 },
    { "NECROSIS_FACTOR",  PT_INT, offsetof(config_info, parameter2), -20, +20, 0 },
    { "GILLYWEED_LEAVES", PT_INT, offsetof(config_info, parameter3), 1, 3, 0 },
    { "INFLATION_FACTOR", PT_INT, offsetof(config_info, parameter4), 1000, 10000, 0 },
    { "EXTRA_CONFIG",     PT_STR, offsetof(config_info, string1), 0, 0, 64 },
    { "USER_NAME",        PT_STR, offsetof(config_info, string2), 0, 0, 16 },
    { "GIZMOTRON_LABEL",  PT_STR, offsetof(config_info, string3), 0, 0, 32 },
};

Vous pouvez maintenant écrire une fonction générale qui lit les lignes du fichier de configuration, en supprimant les commentaires et les lignes vides. Il isole ensuite le nom du paramètre et le recherche dans la table desc_configuration (que vous pouvez trier de manière à pouvoir effectuer une recherche binaire - plusieurs questions SO répondant à cette adresse). Lorsqu'il trouve le bon enregistrement config_desc , il peut transmettre la valeur trouvée et l'entrée config_desc à l'une des deux routines - l'une pour le traitement des chaînes, l'autre pour le traitement des entiers.

La partie clé de ces fonctions est:

static int validate_set_int_config(const config_desc *desc, char *value)
{
    int *data = (int *)((char *)&main_configuration + desc->offset);
    ...
    *data = atoi(value);
    ...
}

static int validate_set_str_config(const config_desc *desc, char *value)
{
    char **data = (char **)((char *)&main_configuration + desc->offset);
    ...
    *data = strdup(value);
    ...
}

Cela évite d'avoir à écrire une fonction distincte pour chaque membre distinct de la structure.

Un opérateur __offsetof intégré a pour but de permettre au fournisseur du compilateur de continuer à # définir une macro offsetof () tout en le faisant fonctionner avec des classes qui définissent un opérateur unaire. La définition typique de la macro C de offsetof () ne fonctionnait que lorsque (& amp; lvalue) renvoyait l'adresse de cette rvalue. C'est à dire.

#define offsetof(type, member) (int)(&((type *)0)->member) // C definition, not C++
struct CFoo {
    struct Evil {
        int operator&() { return 42; }
    };
    Evil foo;
};
ptrdiff_t t = offsetof(CFoo, foo); // Would call Evil::operator& and return 42

Comme @litb, dit: le décalage en octets d'un membre struct / class. En C ++, il existe des cas où il est indéfini, au cas où le compilateur se plaindrait. IIRC, une façon de le mettre en œuvre (en C, au moins) est de faire

#define offsetof(type, member) (int)(&((type *)0)->member)

Mais je suis sûr qu'il y a des problèmes, mais je laisserai cela au lecteur intéressé de le signaler ...

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