qual è lo scopo e il tipo di restituzione dell'operatore __builtin_offsetof?

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

  •  03-07-2019
  •  | 
  •  

Domanda

Qual è lo scopo dell'operatore __builtin_offsetof (o dell'operatore _FOFF in Symbian) in C ++?

Inoltre cosa restituisce? Pointer? Numero di byte?

È stato utile?

Soluzione

È un builtin fornito dal compilatore GCC per implementare la macro offsetof specificata dagli standard C e C ++:

GCC - offsetof

Restituisce l'offset in byte a cui si trova un membro di una struttura / unione POD.

Esempio:

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 fornisce un bell'esempio di dove puoi usarlo. Ricordo di averlo visto utilizzato per implementare elenchi intrusivi (elenchi i cui elementi di dati includono gli indicatori next e prev stessi), ma non ricordo dove sia stato utile implementarlo, purtroppo.

Altri suggerimenti

Come sottolinea @litb e mostra @JesperE, offsetof () fornisce un offset intero in byte (come valore size_t ).

Quando potresti usarlo?

Un caso in cui potrebbe essere rilevante è un'operazione guidata da una tabella per la lettura di un numero enorme di diversi parametri di configurazione da un file e l'inserimento dei valori in una struttura di dati altrettanto enorme. Riducendo enormemente fino a SO banale (e ignorando un'ampia varietà di pratiche del mondo reale necessarie, come la definizione dei tipi di struttura nelle intestazioni), intendo che alcuni parametri potrebbero essere numeri interi e altre stringhe, e il codice potrebbe apparire debolmente come:

#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 },
};

Ora puoi scrivere una funzione generale che legge le righe dal file di configurazione, scartando i commenti e le righe vuote. Quindi isola il nome del parametro e lo cerca nella tabella desc_configuration (che potresti ordinare in modo da poter effettuare una ricerca binaria - più domande SO indirizzano quella). Quando trova il record config_desc corretto, può passare il valore trovato e la voce config_desc a una delle due routine: una per l'elaborazione delle stringhe, l'altra per l'elaborazione dei numeri interi.

La parte fondamentale di queste funzioni è:

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);
    ...
}

Questo evita di dover scrivere una funzione separata per ciascun membro separato della struttura.

Lo scopo di un operatore __offsetof incorporato è che il fornitore del compilatore può continuare a #definire una macro offsetof (), ma farlo funzionare con classi che definiscono operatore unario & amp ;. La tipica definizione di macro C di offsetof () ha funzionato solo quando (& amp; lvalue) ha restituito l'indirizzo di quel valore. Cioè

#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

Come ha detto @litb: l'offset in byte di un membro struct / class. In C ++ ci sono casi in cui non è definito, nel caso in cui il compilatore si lamenterà. IIRC, un modo per implementarlo (almeno in C) è fare

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

Ma sono sicuro che ci sono problemi, ma lo lascerò al lettore interessato per sottolineare ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top