2008-12-30 5 views

Respuesta

8

Es una orden interna proporcionada por el compilador GCC para implementar la macro offsetof especificado por el estándar de C++ y C:

GCC - offsetof

Se devuelve el desplazamiento en bytes que un miembro de una estructura POD/la unión está en.

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 ofrece un buen ejemplo de que se puede usar. Recuerdo haberlo utilizado para implementar listas intrusivas (listas cuyos elementos de datos incluyen los punteros siguiente y anterior), pero no recuerdo dónde fue útil implementarlo, lamentablemente.

+0

Supongo que lo que es útil es que los nodos intrusos contienen punteros al nodo en el "próximo" objeto. Al utilizar la lista, debe obtener desde el nodo hasta la base del objeto, por lo que resta resta de bytes (algo) de bytes del valor del puntero y reinterpreta_cast. –

+0

Todo muy no portátil en C++, por supuesto, pero cumple con el trabajo en C. –

2

Como @litb, dijo: el desplazamiento en bytes de un miembro struct/class. En C++ hay casos en los que no está definido, en caso de que el compilador se queje. IIRC, una manera de ponerla en práctica (en C, por lo menos) es hacer

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

Pero estoy seguro de que hay problemas, pero lo dejo al lector interesado a señalar ...

+0

Comportamiento indefinido, incluso en C. Múltiples razones, incluso: redefinición de una macro estándar y deref de NULL. Común en stdlib, sin embargo, ya que está obligado por diferentes reglas. – MSalters

+0

@MSalters - JesperE es correcto. Vea la definición en stddef.h en el código fuente del kernel de Linux: http://lxr.linux.no/#linux+v2.6.31/include/linux/stddef.h#L24 –

12

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

¿Cuándo podría usarlo?

Un caso en el que podría ser relevante es una operación de tabla para leer una enorme cantidad de parámetros de configuración diversos 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 en el mundo real, como definir tipos de estructura en encabezados), quiero decir que algunos parámetros podrían ser enteros y otras cadenas, y el código podría verse débilmente como

#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 para que pueda hacer una búsqueda binaria, así que varias preguntas SO). Cuando encuentra el registro correcto config_desc, puede pasar el valor que encontró y la entrada config_desc a una de dos rutinas: una para el procesamiento de cadenas y la otra para procesar 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 elemento separado de la estructura.

+1

Si quería ser realmente malvado, podría usar una tabla hash que contenga los nombres e índices de los parámetros en 'desc_configuration'. Ejemplo realmente increíble por cierto. –

+1

@Robert: este ejemplo se basa estrechamente en la lectura de datos de un archivo de configuración en una estructura de big data y la inversión del proceso. No me molestaré en explicar cómo se hace actualmente: basta con decir que hay 300 parámetros, aproximadamente 4500 líneas de código en la función que lo maneja todo, y mucha repetición. No estoy a cargo del código, por desgracia. –

+0

Vea también: http://stackoverflow.com/questions/1445762/need-way-to-alter-common-fields-in-different-structs –

7

El propósito de un operador __offsetof incorporado es que el proveedor del compilador pueda continuar definiendo una macro offsetof(), pero que funcione con las clases que definen el operador unario &. La definición de macro de C típica de offsetof() solo funcionaba cuando (& lvalue) devolvía la dirección de ese valor de r. 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 
Cuestiones relacionadas