2012-01-02 25 views
7

Supongamos que tenemos dos estructuras:Struct compatibilidad puntero

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

¿Es seguro enviar contenido desde Struct2 * a Struct1 *? ¿Qué dice la especificación de ANSI sobre esto? Sé que algunos compiladores tienen la opción de reordenar los campos de las estructuras para optimizar el uso de la memoria, lo que puede hacer que las dos estructuras sean incompatibles. ¿Hay alguna forma de asegurarse de que este código sea válido, independientemente de la bandera del compilador?

¡Gracias!

+2

* Reordenar * los miembros no está permitido por la norma AFAIK. Sin embargo, creo que sería posible insertar diferentes cantidades de relleno. – delnan

+0

@delnan Entonces, ¿qué estructura 'embalaje' solo desactivará la alineación? ¡Gracias, no lo sabía! – Waneck

Respuesta

5

tipos punteros struct siempre tienen la misma representación en C.

(C99, 6.2.5p27) "todos los punteros de estructurar tipos tendrán los mismos requisitos de representación y de alineación como entre sí."

y miembros en los tipos de estructura están siempre en el orden en C.

(C99, 6.7.2.1p5) "una estructura es un tipo que consta de una secuencia de miembros, cuyo almacenamiento se asigna en una secuencia ordenada "

+1

Esto no responde la pregunta; incluso con estas limitaciones, podría seguir siendo una violación de aliasing. Sin embargo, bajo ciertas condiciones, el estándar C permite explícitamente lo que OP quiere. –

+0

Muchas gracias por estas citas de la especificación de ANSI. ¡Esto para mí deja en claro que esto es seguro! – Waneck

+0

@R. ¿Que condiciones? – Waneck

2

Es muy probable que funcione. Pero tiene mucha razón al preguntar cómo puede estar seguro de que este código será válido. Entonces: en algún lugar de tu programa (al comienzo tal vez) inserte un montón de declaraciones ASSERT que se aseguren de que offsetof(Struct1.a_short) sea igual a offsetof(Struct2.a_short) etc. Además, algún programador que no sea un día podría modificar una de estas estructuras pero no la otra, entonces mejor salvo que lo siento

+0

El uso de afirmaciones estáticas sería mucho mejor ... –

+0

Gracias Mike, seguro agregaré algunos aseguramientos para asegurar esto! – Waneck

+0

@R .. ¿afirma estático? No sabía que existían.[Lo busqué y descubrí] (http://stackoverflow.com/questions/3385515/static-assert-in-c). Tienes razón, gracias. –

3

Es seguro, hasta donde yo sé.

Pero es mucho mejor, si es posible, hacer:

typedef struct { 
    Struct1 struct1; 
    short another_short; 
} Struct2; 

después, se lo he dicho al compilador que Struct2 comienza con una instancia de Struct1, y puesto que un puntero a una estructura siempre apunta en su primer miembro, puede tratar Struct2 * como Struct1 *.

+0

bueno, si hay la menor posibilidad de que un día se encuentre 'offsetof (Struct1.a_short)' en NO es igual a 'offsetof (Struct2.a_short)', entonces hay una posibilidad igual de que un día 'offsetof (Struct2.struct1)' no se encuentre igual a cero. (Lo que significaría que '& struct2! = (Struct2 *) y struct2.struct1'). –

+0

De hecho, ¡esta forma es mucho mejor! :) ¡Gracias! – Waneck

+0

Si struct1 y struct2 colocaron primero la alineación de 32 bits "int" primero y "int", entonces ambos tipos de estructura podrían tener 8 bytes, pero su forma alternativa de Struct2 necesitaría 12 bytes. Si los compiladores respetan la regla de secuencia inicial común, cualquiera de las formas debe ser válida (y la de 8 bytes sería más eficiente), pero incluso cuando se invoca en el modo C89, gcc ya no mantiene las garantías de C89 excepto cuando '-fno-strict- aliasing' bandera se utiliza. – supercat

1

Sí, está bien hacerlo!

Un programa de ejemplo es el siguiente.

#include <stdio.h> 

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

int main(void) 
{ 

    Struct2 s2 = {1, 2, 3}; 
    Struct1 *ptr = &s2; 
    void *vp = &s2; 
    Struct1 *s1ptr = (Struct1 *)vp; 

    printf("%d, %d \n", ptr->a_short, ptr->id); 
    printf("%d, %d \n", s1ptr->a_short, s1ptr->id); 

    return 0; 
}