¿Cuál es el propósito y el tipo de retorno del operador __builtin_offsetof?

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

  •  03-07-2019
  •  | 
  •  

Pregunta

¿Cuál es el propósito del operador __builtin_offsetof (u operador _FOFF en Symbian) en C ++?

Además, ¿qué devuelve? ¿Puntero? ¿Número de bytes?

¿Fue útil?

Solución

Es un componente incorporado proporcionado por el compilador GCC para implementar la macro offsetof que se especifica en el estándar C y C ++:

GCC - offsetof

Devuelve el desplazamiento en bytes en que se encuentra un miembro de una estructura / unión POD.

Muestra:

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 proporciona un buen ejemplo de dónde puedes usarlo. Recuerdo haberlo visto para implementar listas intrusivas (listas cuyos elementos de datos incluyen los punteros siguientes y previos en sí mismos), pero lamentablemente no recuerdo dónde fue útil para implementarlo.

Otros consejos

Como @litb señala y @JesperE muestra, offsetof () proporciona un desplazamiento de entero en bytes (como un valor de size_t ).

¿Cuándo podrías usarlo?

Un caso en el que podría ser relevante es una operación dirigida por tablas para leer una enorme cantidad de diversos parámetros de configuración de un archivo y rellenar los valores en una estructura de datos igualmente enorme. Reducir enormemente a SO trivial (e ignorar una amplia variedad de prácticas necesarias del mundo real, como definir tipos de estructuras en encabezados), quiero decir que algunos parámetros podrían ser enteros y otras cadenas, y el código podría parecerse ligeramente a:

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

Ahora puede escribir una función general que lea líneas del archivo de configuración, descartando comentarios y líneas en blanco. A continuación, aísla el nombre del parámetro y lo busca en la tabla desc_configuration (que puede ordenar de modo que pueda hacer una búsqueda binaria; varias preguntas de SO tratan esa cuestión). Cuando encuentra el registro config_desc correcto, puede pasar el valor que encontró y la entrada config_desc a una de las dos rutinas: una para procesar cadenas y la otra para procesar números enteros.

La parte clave de esas funciones es:

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

Esto evita tener que escribir una función separada para cada miembro separado de la estructura.

El propósito de un operador __offsetof incorporado es que el proveedor del compilador pueda #definar una macro offsetof (), pero que funcione con clases que definen un operador unario & amp ;. La definición de macro de C típica de offsetof () solo funcionó cuando (& amp; lvalue) devolvió la dirección de ese rvalue. Es decir.

#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

Como @litb, dijo: el desplazamiento en bytes de una estructura / miembro de clase. En C ++ hay casos en los que no está definido, en caso de que el compilador se queje. IIRC, una forma de implementarlo (en C, al menos) es hacerlo

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

Pero estoy seguro de que hay problemas con esto, pero se lo dejo al lector interesado para que señale ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top