Articulo de referencia

desplazamiento de

La macro de C es una característica de la biblioteca ANSI C que se encuentra en stddef.h . Evalúa como el desplazamiento (en bytes) de un miembro dado dentro de un tipo de estru...

La macro de C es una característica de la biblioteca ANSI C que se encuentra en stddef.h . Evalúa como el desplazamiento (en bytes) de un miembro dado dentro de un tipo de estructura o unión , una expresión de tipo size_t . La macro toma dos parámetros : el primero es el nombre de una estructura o unión, y el segundo es el nombre de un subobjeto de la estructura/unión que no es un campo de bits . No puede describirse como un prototipo de C. [ 1 ]offsetof()offsetof()

Implementación

La implementación "tradicional" de la macro se basaba en que el compilador obtuviera el desplazamiento de un miembro especificando una estructura hipotética que comienza en la dirección cero:

#define offsetof(st, m) \  ((size_t)&(((st*)0)->m))

Esto puede entenderse como tomar un puntero nulo de tipo estructura `st` y luego obtener la dirección del miembro `m` dentro de dicha estructura. Si bien esta implementación funciona correctamente en muchos compiladores, ha generado cierto debate sobre si esto constituye un comportamiento indefinido según el estándar C, [ 2 ] ya que parece implicar una desreferenciación de un puntero nulo . También tiende a producir diagnósticos confusos del compilador si uno de los argumentos está mal escrito.

Una alternativa es:

#define offsetof(st, m) \  ((size_t)((char*)&((st*)0)->m - (char*)0))

Es posible que se especifique de esta manera porque el estándar no indica que la representación interna del puntero nulo se encuentre en la dirección cero. Por lo tanto, es necesario calcular la diferencia entre la dirección del miembro y la dirección base.

Algunos compiladores modernos (como GCC ) definen la macro usando una forma especial (como una extensión del lenguaje), por ejemplo [ 3 ].

#define offsetof(st, m) \  __builtin_offsetof(st, m)

Esta función integrada es especialmente útil con clases de C++ que declaran un operador unario personalizado & . [ 4 ]

Uso

Es útil al implementar estructuras de datos genéricas en C. Por ejemplo, el kernel de Linux utiliza offsetof() para implementar container_of() , que permite que algo como un tipo mixin encuentre la estructura que lo contiene: [ 5 ]

#define CONTAINER_OF(ptr, Type, member) ({ \  const typeof(((Type*)0)->member) *__mptr = (ptr); \  (Type*)((char*)__mptr - offsetof(Type, member));})

Esta macro se utiliza para recuperar una estructura contenedora a partir de un puntero a un elemento anidado, como esta iteración de una lista enlazada de objetos MyStruct :

struct MyStruct { const char * name ; struct ListNode list ; };estructura externa ListNode * listNext ( estructura ListNode * );estructura ListNode * actual = /* ... */ ;while ( current ) { struct MyStruct * element = CONTAINER_OF ( current , struct MyStruct , list ); printf ( "%s \n " , element- > name ); current = listNext ( & element- > list ); }

La implementación de container_of en el kernel de Linux utiliza una extensión de GNU C llamada expresiones de sentencia . [ 6 ] Es posible que se haya utilizado una expresión de sentencia para garantizar la seguridad de tipos y, por lo tanto, eliminar posibles errores accidentales. Sin embargo, existe una forma de implementar el mismo comportamiento sin utilizar expresiones de sentencia y, al mismo tiempo, garantizar la seguridad de tipos:

#define CONTAINER_OF(ptr, Type, member) ((Type*)((char*)(1 ? (ptr) : &((Type*)0)->member) - offsetof(Type, member)))

A primera vista, esta implementación puede parecer más compleja de lo necesario, y el uso inusual del operador condicional puede parecer fuera de lugar. Es posible una implementación más sencilla:

#define CONTAINER_OF(ptr, Type, member) ((Type*)((char*)(ptr) - offsetof(Type, member)))

Esta implementación también cumpliría el mismo propósito, sin embargo, tiene una omisión fundamental en comparación con la implementación original del kernel de Linux: el tipo ptrnunca se comprueba con respecto al tipo del miembro.

En la implementación con verificación de tipos mencionada anteriormente, la verificación se realiza mediante el uso inusual del operador condicional. Las restricciones del operador condicional especifican que si ambos operandos son punteros a un tipo, deben ser punteros a tipos compatibles. En este caso, a pesar de que el valor del tercer operando de la expresión condicional nunca se utilizará, el compilador debe realizar una verificación para asegurar que (ptr)ambos &((Type*)0)->membersean punteros de tipos compatibles.

Limitaciones

El uso de offsetofestá limitado a tipos POD en C++98 , clases de diseño estándar en C++11 , [ 7 ] y se admiten condicionalmente más casos en C++17 , [ 8 ] de lo contrario tiene un comportamiento indefinido. Si bien la mayoría de los compiladores generarán un resultado correcto incluso en casos que no respetan el estándar, hay casos límite en los que offsetof producirá un valor incorrecto, generará una advertencia o error en tiempo de compilación, o directamente bloqueará el programa. Este es el caso especialmente para la herencia virtual. [ 9 ] El siguiente programa generará varias advertencias e imprimirá resultados obviamente sospechosos cuando se compile con gcc 4.7.3 en una arquitectura amd64:

#include <cstddef>importar std ;struct Base { int a ; virtual void dummy () {} };struct Derived : virtual public Base { int b ; };int main () { std :: println ( "offsetof(Base, a): {}" , offsetof ( Base , a )); std :: println ( "offsetof(Derived, b): {}" , offsetof ( Derived , b )); return 0 ; }

El resultado es:

desplazamiento de (Base, a) : 8 desplazamiento de (Derivado, b) : 8 

Referencias

  1. ^ "offsetof reference" . MSDN . Consultado el 19 de septiembre de 2010 .
  2. ^ "¿Provoca &((struct name *)NULL -> b) un comportamiento indefinido en C11?" . Consultado el 07-02-2015 .
  3. ^ "Referencia de desplazamiento de GCC" . Free Software Foundation . Consultado el 19 de septiembre de 2010 .
  4. ^ "¿Cuál es el propósito y el tipo de retorno del operador __builtin_offsetof?" . Consultado el 20 de octubre de 2012 .
  5. ^ Greg Kroah-Hartman (junio de 2003). "container_of()" . Linux Journal . Consultado el 19 de septiembre de 2010 .
  6. ^ "Declaraciones y afirmaciones en expresiones" . Free Software Foundation . Consultado el 1 de enero de 2016 .
  7. ^ "offsetof reference" . cplusplus.com . Consultado el 1 de abril de 2016 .
  8. ^ "offsetof reference" . cppreference.com . Consultado el 20 de julio de 2020 .
  9. ^ Steve Jessop (julio de 2009). "¿Por qué no se puede usar offsetof en estructuras que no son POD en C++?" . Stack Overflow . Consultado el 1 de abril de 2016 .
Obtenido de " https://en.wikipedia.org/w/index.php?title=Offsetof&oldid=1319377661 "