2012-01-23 16 views
6

Si tengo la siguiente estructura:¿Es una estructura de un elemento compatible con el elemento en sí?

struct Foo { int a; }; 

Es el código abajo conforme con el estándar de C++? Quiero decir, ¿no puede generar un "Comportamiento Indefinido"?

Foo foo; 
int ifoo; 

foo = *reinterpret_cast<Foo*>(&ifoo); 

void bar(int value); 

bar(*reinterpret_cast<int*>(&foo)); 

auto fptr = static_cast<void(*)(...)>(&bar); 
fptr(foo); 
+1

Estoy bastante seguro de que la estructura puede tener requisitos de alineación más estrictos que la int, por lo que te encuentras con UB cuando reinterpretas de 'int' a' Foo'. No estoy seguro de la otra forma. – avakar

Respuesta

10

9,2/20 en N3290 dice

Un puntero a un objeto struct estándar-layout, adecuadamente convertidos utilizando un reinterpret_cast, los puntos a su miembro inicial (o si ese miembro es un campo de bits, a continuación, a la unidad en la que reside) y viceversa.

Y su Foo es una clase de diseño estándar.

Por lo tanto, su segundo modelo es el correcto.

No veo ninguna garantía de que la primera sea correcta (y he usado arquitectura donde un char tenía una restricción de alineación más débil que una estructura que contiene solo un carácter, en una arquitectura así, sería problemático). La garantía estándar es que si tiene un puntero a int que realmente apunta al primer elemento de una estructura, puede reinterpretarlo de nuevo como puntero a la estructura.

Del mismo modo, no veo nada que podría definir tu tercera si era una reinterpretación_cast (estoy bastante seguro de que algunos ABI usan convenciones diferentes para pasar estructuras y tipos básicos, por lo que es muy sospechoso y necesitaría una mención clara en el estándar para aceptarlo) y estoy bastante seguro de que nada permite static_cast entre punteros a las funciones.

+0

¿Significa que struct no puede tener una alineación más estricta que su miembro inicial? Si es así, ¿eso significa que los punteros a struct deben ser al menos tan anchos como 'char *'? – avakar

+0

Ah, y +1 para la única respuesta que realmente cita el estándar. – avakar

+0

@avakar, oops, leí mal el primer ejemplo, arreglé mi respuesta. Gracias. – AProgrammer

2

Foo es una estructura de datos de edad-llano, lo que significa que no contiene más que los datos se almacenan de forma explícita en el mismo. En este caso: un int. Por lo tanto, el diseño de memoria para un int y Foo es el mismo.

Puede encasillar de uno a otro sin problemas. Si es una idea inteligente usar este tipo de cosas es una pregunta diferente.

PS: Esto generalmente funciona, pero no necesariamente debido a las diferentes restricciones de alineación. Ver la respuesta de AProgrammer.

+0

Pero ... 'reinterpret_cast'? De Verdad? –

+0

@MrLister: ¿Qué pasa con 'reinterpret_cast'? Es exactamente para este tipo de cosas: reinterpretar una cosa como otra. – Xeo

+1

El estándar requiere que un puntero a una estructura POD sea idéntico al puntero a su primer elemento. – spraff

4

Siempre que acceda solo al primer elemento de una estructura, se considera seguro, ya que no hay relleno antes del primer miembro de una estructura. De hecho, se utiliza este truco, por ejemplo, en el tiempo de ejecución Objecive-C, donde un tipo de puntero genérico se define como:

typedef struct objc_object { 
    Class isa; 
} *id; 

y en tiempo de ejecución, objecs reales (que todavía son punteros struct desnudo) tienen memoria diseños como este:

struct { 
    Class isa; 
    int x; // random other data as instance variables 
} *CustomObject; 

y el tiempo de ejecución accede a la clase de un objeto real con este método.

+1

Esa técnica es similar a lo que se usó en [BOOPSI] (http://en.wikipedia.org/wiki/BOOPSI) en el viejo Amiga. –

+0

Usted es técnicamente correcto, pero solo ha respondido la mitad de la pregunta. – avakar

Cuestiones relacionadas