2009-09-21 11 views
7

Existe esta macro offsetof en C/C++ que le permite obtener el desplazamiento de dirección de un miembro en una estructura POD. Para ver un ejemplo de la C FAQ:Usos legítimos del offsetof Macro en C/C++

struct foo { 
int a; 
int b; 
}; 

struct foo; 

/* Set the b member of foo indirectly */ 
*(int *)((char *)foo + offsetof(b)) = 0xDEADBEEF; 

Ahora bien, esto sólo parece mal a mí y no se puede ver muchos usos legítimos de esta macro.

Un ejemplo de fiar que he visto es su uso en el container_of macro en el núcleo de Linux para obtener la dirección de un objeto estructuras padres incrustado:

/* get the address of the cmos device struct in which the cdev 
    structure which inode points to is embedded */ 
struct cmos_dev *cmos_devp = 
    container_of(inode->i_cdev, struct cmos_dev, cdev); 

¿Qué otros usos legítimos existen para esta macro? ¿Cuándo debería no usar esta macro?

EDIT Hasta ahora this answer a una pregunta de SO diferente es la mejor que he visto hasta ahora.

+0

Su primer uso es legítimo en C, pero en C++ punteros a miembro bastante eliminar tanto la necesidad. –

+1

Aquí es alguna otra pregunta sobre ella: http://stackoverflow.com/questions/400116/what-is-the-purpose-and-return-type-of-the-builtinoffsetof-operator –

Respuesta

4

Bien ... En C, es muy útil para cualquier lugar donde necesite código para describir una estructura de datos. Lo he usado, p. para hacer la GUI generada en tiempo de ejecución para las opciones de configuración.

Esto funcionaba así: un comando que necesita opciones define una estructura local que contiene sus opciones, y luego describe esa estructura para el código que genera la GUI, usando offsetof para indicar dónde están los campos. El uso de compensaciones en lugar de direcciones absolutas permite que el código de la GUI funcione con cualquier instancia de la estructura, no solo una.

Esto es un poco difícil de esbozar rápidamente en un ejemplo (lo intenté), pero como los comentarios indican que hay un ejemplo en orden, lo intentaré de nuevo.

Supongamos que tenemos un módulo autónomo, llamado "comando", que implementa alguna acción en la aplicación. Este comando tiene un conjunto de opciones que controlan su comportamiento general, que debe exponerse al usuario a través de una interfaz gráfica de usuario. A los fines de este ejemplo, supongamos que la aplicación es un administrador de archivos, y el comando podría ser, p. "Dupdo".

La idea es que el código de copia viva en un archivo C y el código GUI en otro, y que el código GUI no necesite estar codificado para "admitir" las opciones del comando de copia. En su lugar, definimos las opciones en el archivo de copia, así:

struct copy_options 
{ 
    unsigned int buffer_size;  /* Number of bytes to read/write at a time. */ 
    unsigned int copy_attributes; /* Attempt to copy attributes. */ 
    /* more, omitted */ 
}; 

static struct copy_options options; /* Actual instance holding current values. */ 

A continuación, el comando de copia registra sus valores de configuración con el módulo de interfaz gráfica de usuario:

void copy_register_options(GUIModule *gui) 
{ 
    gui_command_begin(gui, "Copy"); 
    gui_command_add_unsigned_int(gui, "Buffer size", offsetof(struct copy_options, buffer_size)); 
    gui_command_add_boolean(gui, "Copy attributes", offsetof(struct copy_options, copy_attributes)); 
    gui_command_end(gui); 
} 

Entonces, digamos que el usuario pide a establece las opciones del comando de copia. entonces podemos copiar primero las opciones actuales, para apoyar la cancelación, y pedir al módulo de interfaz gráfica de usuario para un cuadro de diálogo controles de retención, construido en tiempo de ejecución, adecuada para la edición de las opciones de este comando:

void copy_configure(GUIModule *gui) 
{ 
    struct copy_options edit = options; 

    /* Assume this opens a modal dialog, showing proper controls for editing the 
    * named command's options, at the address provided. The function returns 1 
    * if the user clicked "OK", 0 if the operation was cancelled. 
    */ 
    if(gui_config_dialog(gui, "Copy", &edit)) 
    { 
    /* GUI module changed values in here, make edit results new current. */ 
    options = edit; 
    } 
} 

Por supuesto, este código se supone las configuraciones son tipos de valores puros, por lo que podemos copiar la estructura mediante una simple asignación de estructuras. Si también admitimos cadenas dinámicas, necesitaríamos una función para hacer la copia. Sin embargo, para los datos de configuración, cualquier cadena probablemente se exprese mejor como una matriz de tamaño estático char en la estructura, lo cual estaría bien.

Nota cómo el hecho de que el módulo de interfaz gráfica de usuario sólo se sabe dónde vive cada valor expresados ​​como un desplazamiento nos permite proporcionar la función de diálogo con una copia temporal en la pila. Si en cambio hubiéramos configurado el módulo GUI con punteros directos para cada campo, esto no sería posible, lo que sería mucho menos flexible.

+0

No sería simplemente usando un puntero también le permiten 'trabajar con cualquier instancia de la estructura'? –

+0

@Robert: Porque solo funcionaría con uno; el que le dio un puntero a. Mediante el uso de compensaciones, puede compilar la GUI para dos instancias diferentes de la misma estructura, sin tener que volver a describir la estructura del código de construcción de la GUI. – unwind

+0

Simplemente no lo obtengo por alguna razón. ¿Podría agregar un ejemplo de código breve a su publicación? –

1

offsetof es relativamente frecuente para la programación de controlador de dispositivo en el que por lo general tiene que escribir en C plano pero a veces necesita algunas "otras" características. Considere que tiene una función de devolución de llamada que obtiene un puntero a alguna estructura. Ahora esta estructura es en sí misma un miembro de otra estructura "externa" más grande. con "offsetof" que tiene la capacidad de cambiar los miembros de la estructura "exterior" cuando se tiene acceso sólo al miembro "interior".

Algo como esto:

struct A 
{ 
int a1; 
int a2; 
}; 

struct B 
{ 
int b1; 
int b2; 
A a; 
}; 

void some_API_callback_func(A * a) 
{ 
//here you do offsetof 
//to get access to B members 
} 

Por supuesto, esto es peligroso si tiene posibilidad de que una estructura no se utiliza como parte de la estructura B. Pero en muchos lugares donde marco para "some_API_callback_func" está bien documentada esto funciona bien

+0

Gracias, pero esto es exactamente lo que ya describí con respecto a la macro container_of en el kernel de Linux. –

2

Básicamente, cualquier cosa que harías con un puntero a miembro (T::*) en C++ es un buen candidato para el uso de offsetof en C. Por esa razón, offsetof es mucho más raro en C++.

Ahora bien, esto es, por supuesto, un poco circular, así que aquí están algunos ejemplos:

funciones de clasificación
  • semi-genéricas para estructuras. qsort usa una devolución de llamada, que no es ideal. A menudo, solo debe ordenar por el orden natural de un miembro, p. el tercero int en una estructura. Una hipotética qsort_int podría aceptar un argumento offsetof para este propósito.
  • Del mismo modo, es posible escribir una macro extract de tal manera que se puede decir int out[10]; extract(int, &MyFoo[0], &MyFoo[10], out, offsetof(struct Foo, Bar));
5

Una de las maneras que he usado en sistemas embebidos es donde tengo una estructura que representa la disposición de no volátil memoria (ej. EEPROM), pero donde no quiero crear realmente una instancia de esta estructura en RAM. Puede usar varios trucos de macros agradables para permitirle leer y escribir campos específicos de la EEPROM, donde offsetof hace el trabajo de calcular la dirección de un campo dentro de la estructura.

Con respecto al "mal", debe recordar que muchas cosas que tradicionalmente se hacían en programación 'C', especialmente en plataformas de recursos limitados, ahora parecen piratería malvada cuando se ven desde el entorno lujoso de la informática moderna .

5

Un uso legítimo de offsetof() es determinar la alineación de un tipo:

#define ALIGNMENT_OF(t) offsetof(struct { char x; t test; }, test) 

Puede ser un bajo nivel de bit a necesitar la alineación de un objeto, pero en cualquier caso me gustaría considerar este un uso legítimo.