2011-12-15 14 views
10

Por favor, considere el siguiente código:evitar la trampa de representación con memcpy

float float_value = x; // x is any valid float value 
int int_value = 0; 
size_t size = sizeof(int) < sizeof(float) ? sizeof(int) : sizeof(float); 
memcpy(&int_value, &float_value, size); 

Por lo que yo sé que esto podría resultar en una representación trampa. Mis preguntas:

  1. ¿Es eso cierto?
  2. Si no, ¿por qué?
  3. Si no, ¿hay alguna otra manera de evitar una posible representación de trampas?
+0

Básicamente parece un mal comportamiento indefinido. Int y float pueden ser de diferentes tamaños. ¿Qué estás realmente tratando de lograr? – EvilTeach

+2

¿Estás tratando de ver la representación binaria de un flotador? memcpy() es una solución de bomba nuclear donde el simple martillo de una asignación con un tipo de transmisión haría. –

+1

Puede usar un int sin firmar y evitar cualquier problema. – TJD

Respuesta

10

La forma sancionada que no producirá ninguna representación trampa es

unsigned char obj[sizeof float]; 
memcpy(obj, &float_value, sizeof float); 

continuación, puede utilizar los bytes de la representación de objetos para construir su int deseado.

Pero usar enteros de ancho fijo como los mencionados por Stephen Canon es mejor, a menos que tenga un tamaño extraño de float.

+0

C99: http://blackshell.com/~msmud/cstd.html#6.2.6.1 – u0b34a0f6ae

+0

En realidad, para el propósito específico de hash, me gusta bastante esta solución. –

+0

@ kaizer.se Gracias por el enlace al estándar. –

2

Sí, eso podría dar como resultado una representación de trampa. En cuanto a cómo evitarlo:

  • Afirmar que sizeof(int32_t) == sizeof(float)
  • int32_t uso en lugar de int.

Los tipos enteros de ancho fijo pueden no admitir representaciones de capturas. Específicamente, el estándar requiere que no tengan bits de relleno, y no se puede tener una representación de trampa sin relleno.

+0

¿estás seguro de eso? yo solo pensé que * * charla sin signo * nunca atrapa. ¿podrías citar C99? ¡gracias por la respuesta! – Johannes

+0

@Johannes: §7.18.1.1 "El nombre de typedef intN_t designa un tipo de entero con signo con ancho N, sin bits de relleno y una representación de complemento de dos." Como no hay bits de relleno, todos los N bits son * valores * bits, por lo tanto, cada combinación de bits tiene un valor definido por el estándar y no puede ser una representación de trampa. –

+0

@Stephen, podría tener una representación de trampa sin relleno, es decir, las restricciones que cita seguirán dejando dos valores posibles para 'INTXX_MIN'. Pero el estándar cierra esta laguna al arreglar lo que el valor tiene que ser además. En la copia que tengo, incluso parece que esto se agregó más tarde con una de las actualizaciones. –

3

No necesita memcpy si solo desea inspeccionar los valores allí. Lo más fácil es simplemente echar

unsigned char const* p = &float_object; 

elenco puntero a todos los tipos char siempre está garantizada para dar algo válido con el que se puede hacer aritmética simple. Está seguro mientras desreferencia dentro de los límites dados por sizeof float_object.

Si desea tratar eso como un número, lo más seguro es elegir un entero sin signo de ancho fijo, muy probablemente uint32_t. Si sabe que se cumplen los requisitos de ancho, esto debería darle todo lo que necesita.

Como se mencionó, esto funciona bien siempre que no escriba a través de ese puntero. Entonces, las reglas de aliasing para los punteros pueden hacer que el optimizador salga mal después.