2010-11-12 20 views
49

Utilizo un código donde lanzo una enumeración * a int *. Algo como esto:"desreferencia puntero tipo punteado romperá reglas de alias estricto" advertencia

enum foo { ... } 
... 
foo foobar; 
int *pi = reinterpret_cast<int*>(&foobar); 

Al compilar el código (g ++ 4.1.2), me sale el siguiente mensaje de advertencia:

dereferencing type-punned pointer will break strict-aliasing rules 

Googled este mensaje, y encontró que sólo ocurre cuando aliasing estricta la optimización está activada Tengo las siguientes preguntas:

  • Si dejo el código con esta advertencia, ¿generará un código potencialmente incorrecto?
  • ¿Hay alguna forma de evitar este problema?
  • Si no existe, ¿es posible desactivar el alias estricto desde el archivo de origen (porque no quiero desactivarlo para todos los archivos de origen y no quiero crear una regla de Makefile separada para este archivo fuente)?

Y sí, realmente necesito este tipo de aliasing.

+5

¿Por qué no simplemente 'static_cast (foobar)'? – GManNickG

+0

Parece que esta [respuesta] (http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule/99010#99010) tiene lo que estás buscando. –

Respuesta

53

En orden:

  • Sí. GCC asumirá que los punteros no pueden alias. Por ejemplo, si asigna a través de uno y luego lee desde el otro, GCC puede, como optimización, reordenar la lectura y la escritura: he visto que esto sucede en el código de producción, y no es agradable depurarlo.

  • Varios. Puede usar una unión para representar la memoria que necesita para reinterpretar. Puede usar un reinterpret_cast. Puede transmitir a través del char * en el punto donde reinterpreta la memoria: char * se definen como capaces de alias cualquier cosa. Puede usar un tipo que tenga __attribute__((__may_alias__)). Puede desactivar las suposiciones de alias globalmente utilizando -fno-strict-aliasing.

  • __attribute__((__may_alias__)) en los tipos utilizados es probablemente lo más cercano que se puede llegar a deshabilitar la suposición para una sección particular del código.

Para su ejemplo particular, tenga en cuenta que el tamaño de una enumeración está mal definido; GCC generalmente usa el tamaño entero más pequeño que se puede usar para representarlo, por lo que la reinterpretación de un puntero a una enumeración como un entero podría dejarte con bytes de datos no inicializados en el entero resultante. No hagas eso. ¿Por qué no simplemente lanzar a un tipo de entero adecuado?

+11

Solo una advertencia, que es un comportamiento no especificado utilizar una unión escribiendo a un miembro y leyendo de otro, según el estándar de C++. Entonces puede funcionar, puede que no. – JPvdMerwe

+4

@JPvdMerwe: GCC lo admite explícitamente. Lo mismo ocurre con MSVC. – jalf

+16

"emitir vía' char * '": su punto no es incorrecto, pero "a través de" puede ser engañoso. Puede leer los bytes uno por uno a través de un 'char *' o 'unsigned char *'. Pero 'reinterpret_cast (reinterpret_cast (& foobar))' todavía rompe el alias estricto, aunque he "lanzado a través de' char * '". Si X puede alias Y, e Y puede alias Z (Y es el 'char *'), entonces bajo estrictas reglas de aliasing no necesariamente se sigue que X puede alias Z. –

5

¿Has mirado en this answer?

La regla estricta aliasing hace que este instalación ilegal, dos tipos no relacionados no puede apuntar a la misma memoria. Solo char * tiene este privilegio. Desafortunadamente, todavía se puede codificar esta forma , tal vez obtener algunas advertencias, pero tiene compila bien.

10

¿Pero por qué estás haciendo esto? Se romperá si sizeof (foo)! = Sizeof (int). El hecho de que una enumeración sea como un número entero no significa que esté almacenada como una sola.

Así que sí, podría generar código "potencialmente" incorrecto.

+0

Lo revisé, son del mismo tamaño. – petersohn

+4

@petersohn Tal vez en su máquina, eso no significa nada. –

+4

@petersohn: ¿y ahora? ¿Siguen siendo del mismo tamaño? No tienes garantía de que solo porque funcionó en un punto, * mantendrá * funcionando. – jalf

2

El alias estricto es una opción del compilador, por lo que debe desactivarlo desde el archivo MAKE.

Y sí, puede generar código incorrecto. El compilador supondrá efectivamente que foobar y pi no están unidos, y supondrá que *pi no cambiará si foobar ha cambiado.

Como ya se ha mencionado, use static_cast en su lugar (y sin punteros).

9

podría utilizar el siguiente código para emitir sus datos: el uso

template<typename T, typename F> 
struct alias_cast_t 
{ 
    union 
    { 
     F raw; 
     T data; 
    }; 
}; 

template<typename T, typename F> 
T alias_cast(F raw_data) 
{ 
    alias_cast_t<T, F> ac; 
    ac.raw = raw_data; 
    return ac.data; 
} 

Ejemplo:

unsigned int data = alias_cast<unsigned int>(raw_ptr); 
+6

Añadiendo 'static_assert (sizeof (T) == sizeof (F)," No se pueden lanzar tipos de diferentes tamaños ");' a 'alias_cast' mejoraría la seguridad de este código. –

+0

Si esto se usa donde T y F son tipos de puntero, ¿no es esto todavía UB? Y si no son tipos de puntero, ¿no es esto lo mismo que 'reinterpret_cast (f)', sin ninguna verificación adicional? – Noein

Cuestiones relacionadas