sirve - Usos legales del offset de macro en C/C++
funciones en c++ (5)
Existe esta compensación macro en C / C ++ que le permite obtener el desplazamiento de la dirección de un miembro en una estructura POD. Para un ejemplo de C Preguntas frecuentes :
struct foo {
int a;
int b;
};
struct foo;
/* Set the b member of foo indirectly */
*(int *)((char *)foo + offsetof(b)) = 0xDEADBEEF;
Ahora esto me parece malvado y no puedo ver muchos usos legítimos de esta macro.
Un ejemplo legítimo que he visto es su uso en la macro container_of en el Kernel de Linux para obtener la dirección de un objeto padre de estructuras incrustadas:
/* 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 hay para esta macro? ¿Cuándo no deberías usar esta macro?
EDITAR Hasta ahora, esta respuesta a una pregunta SO diferente es la mejor que he visto hasta ahora.
Básicamente, cualquier cosa que haría con un puntero al 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 esto es, por supuesto, un poco circular, así que aquí hay algunos ejemplos:
- Funciones de clasificación semicomericas para estructuras.
qsort
usa una devolución de llamada, que no es ideal. A menudo solo tiene que ordenar por el orden natural de un miembro, por ejemplo, el tercerint
en una estructura. Unqsort_int
hipotético podría aceptar unaoffsetof
argumento para este propósito. - Del mismo modo, es posible escribir un macroextracto de manera que se pueda decir
int out[10]; extract(int, &MyFoo[0], &MyFoo[10], out, offsetof(struct Foo, Bar));
int out[10]; extract(int, &MyFoo[0], &MyFoo[10], out, offsetof(struct Foo, Bar));
Bueno ... En C, es muy útil para cualquier lugar donde necesite código para describir una estructura de datos. Lo he usado, por ejemplo, para hacer GUIs generadas en tiempo de ejecución para configurar las opciones.
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, por ejemplo, "Copiar".
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 de la 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. */
Luego, el comando de copia registra sus configuraciones con el módulo GUI:
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 establecer las opciones del comando de copia. Entonces podemos copiar primero las opciones actuales, para dar soporte a la cancelación, y pedirle al módulo de GUI un diálogo que contenga los controles, construidos en tiempo de ejecución, adecuados para editar 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 asume que las configuraciones son tipos de valores puros, por lo que podemos copiar la estructura mediante la 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 expresaría mejor como una matriz de caracteres de tamaño estático en la estructura, lo cual estaría bien.
Observe cómo el hecho de que el módulo GUI solo sepa dónde vive cada valor expresado 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.
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 poco bajo nivel para necesitar la alineación de un objeto, pero en cualquier caso, lo consideraría un uso legítimo.
Una de las formas en que lo he usado en sistemas integrados es donde tengo una estructura que representa el diseño de la memoria no volátil (por ejemplo, EEPROM), pero en la que no quiero realmente crear una instancia de esta estructura en la 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", debes recordar que muchas de las cosas que se hacían tradicionalmente en la programación "C", particularmente en plataformas de recursos limitados, ahora se ven como hackers malvados cuando se ven desde el entorno lujoso de la informática moderna.
offsetof se usa con relativa frecuencia para la programación de controladores de dispositivos, donde generalmente debe escribir en C simple, 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 bien, esta estructura es en sí misma un miembro de otra estructura "exterior" más grande. con "offsetof" tiene la capacidad de cambiar los miembros de la estructura "externa" cuando tiene acceso solo al miembro "interno".
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 tienes la posibilidad de que la estructura A no se use como parte de la estructura B. Pero en muchos lugares donde el marco para "some_API_callback_func" está bien documentado, funciona bien.