2010-05-27 19 views
5

El código base en el trabajo contiene un código que se ve más o menos así:¿Cómo interpretar datos binarios como un número entero?

#define DATA_LENGTH 64 

u_int32 SmartKey::SerialNumber() 
{ 
    unsigned char data[DATA_LENGTH]; 
    // ... initialized data buffer 
    return *(u_int32*)data; 
} 

Este código funciona correctamente, pero GCC da la siguiente advertencia:

warning: dereferencing pointer ‘serialNumber’ does break strict-aliasing rules 

¿Puede alguien explicar esta advertencia? ¿Este código es potencialmente peligroso? ¿Cómo puede ser mejorado?

actualización
Con gracias a la respuesta de James McNellis' me ocurrió la función de utilidad:

template<class T, class Data> 
T BinaryCast(const Data & inData) 
{ 
    T ret; 
    std::copy(&inData[0], &inData[0] + sizeof(ret), reinterpret_cast<char*>(&ret)); 
    return ret; 
} 

u_int32 SmartKey::SerialNumber() 
{ 
    unsigned char data[DATA_LENGTH]; 
    // ... initialized data buffer 
    return BinaryCast<u_int32>(data); 
} 

Siéntase libre de sugerir mejoras!

+0

Probablemente tiene que ver con el puntero que se lanza desde unsigned char * a u_int32 * pero ha pasado mucho tiempo desde que lo hice C++. Si es así, dado que DATA_LENGTH es un múltiplo exacto de 32, no debería haber un problema. –

Respuesta

11

La advertencia se debe a que está infringiendo el strict aliasing rule.

Una forma de hacerlo correctamente sería copiar los bytes de la memoria intermedia data en un objeto u_int32 y devolver ese objeto:

unsigned char data[DATA_LENGTH]; 
// ... initialized data buffer 

u_int32 i; 
assert(sizeof (i) <= DATA_LENGTH); 
std::copy(&data[0], &data[0] + sizeof (i), reinterpret_cast<char*>(&i)); 
return i; 

Esta solución funciona porque en C++ que se permite acceder a cualquier tipo de objeto como una matriz de char.

(std::copy() es en <algorithm>)

+0

Exactamente lo que quería publicar. Aunque mi solución usó memcpy. – PeterK

+1

Incluso si no está utilizando STL, al menos use reinterpret_cast. – user347594

+0

Una sutileza que debe mencionarse cuando se habla de (de) serializar datos de esta manera es la endianidad. Es menos preocupante ahora que Apple usa chips Intel, pero debe tenerse en cuenta para evitar que sus datos se corrompan gravemente. – user168715

2

En los lenguajes C y C++ reinterpretación de memoria ocupada por objeto de un tipo como un objeto de otro tipo es ilegal - que conduce a un comportamiento indefinido. Algunos compiladores usan esta regla para realizar optimizaciones agresivas relacionadas con aliasing. Como consecuencia, su código puede no funcionar como se espera, si realiza la reinterpretación mencionada anteriormente.

En C/C++ está bien reinterpretar cualquier objeto como una matriz de caracteres, pero no está bien tomar una matriz independiente de caracteres y reinterpretar como un objeto de otro tipo. Esto es lo que hace tu código.

Además de los problemas de aliasing, debe tener en cuenta que no se garantiza que una matriz de caracteres automática independiente se alinee correctamente para que se lea como un valor u_int32.

La forma correcta de hacer lo que el código está tratando de hacer es copiar la matriz de origen en un valor u_int32 intermedia usando memcpy

u_int32 SmartKey::SerialNumber() 
{ 
    unsigned char data[DATA_LENGTH]; 
    u_int32 u; 
    // ... 
    memcpy(&u, data, sizeof u); 
    return u; 
} 

Por supuesto, usted tiene que estar seguro de que el orden de bits de la los datos son los mismos que los objetos u_int32 endianness en su plataforma.

-1

No estoy seguro, pero creo que se puede hacer así:

return (u_int32)&data; 
+0

Eso convertirá la dirección de 'datos' en' u_int32'. El OP quiere leer el contenido real almacenado en los primeros elementos de 'datos'. Ni remotamente lo mismo. – AnT

0

Creo que el problema es en realidad en algún lugar de su código para inicializar el elided [] estructura de datos. No creo que tenga nada que ver con tu elenco, lo cual está bien.

Cuestiones relacionadas