Re: Confirm reinterpret_cast if is safe?
On Mar 1, 1:14 am, Joshua Maurice <joshuamaur...@gmail.com> wrote:
On Feb 28, 6:54 pm, Nephi Immortal <immortalne...@gmail.com> wrote:
On Feb 28, 6:33 pm, Joshua Maurice <joshuamaur...@gmail.com> wrote:
On Feb 28, 3:39 pm, Nephi Immortal <immortalne...@gmail.com> wrote:
This is broken by C++ standard. You are reading an __in32 object
through a __int8 lvalue, and that is undefined behavior. At least, it
is UB if __int8 is not a typedef of char nor unsigned char.
I don't know what various implements will actually do with that code.
To fix it, at least the following is allowed by the standard: you can
use "char" or "unsigned char" lvalues to read any POD object.- Hide q=
uoted text -
- Show quoted text -
I think you meant unrecognized keyword. Another C++ =
Compiler than
Microsoft C++ Compiler or Intel C++ Compiler will generate an error
message to state undeclared __int8, __int16, and __int32.
I assume that you suggest:
typedef unsigned char size_8;
typedef unsigned short size_16;
typedef unsigned long size_32;
instead of
typedef unsigned __int8 size_8;
typedef unsigned __int16 size_16;
typedef unsigned __int32 size_32;
Can you guarantee to be sure if my source code works wi=
thout any
undefined behavior on IA-32, IA-64, and x64 machine? Other machines
require different definitions.
For example
#if defined( __INTEL__ ) || defined( __AMD__ )
size_32 dword = 0x123456U;
size_8 &L = *reinterpret_cast< size_8* >( &dword );
size_8 &H = *( reinterpret_cast< size_8* >( &dword ) =
+ 1 );
size_8 &B = *( reinterpret_cast< size_8* >( &dword ) =
+ 2 );
size_16 &W = *reinterpret_cast< size_16* >( &dword );
#else
size_32 dword = 0x123456U;
size_8 L = dword & 0xFFU;
size_8 H = ( dword >> 8 ) & 0xFFU;
size_8 B = ( dword >> 16 ) & 0xFFU;
size_16 W = dword & 0xFFFFU;
Possible typo: L, H, B, W are not lvalue references.
#endif. If your compiler doesn't spit out an error, something is very
wrong.
Why wouldn't you just use the second form? That would be a much
preferred way. Check the assembly yourself - but I would expect/hope
that it should be compiled down to the same thing with optimization.
It's only the same on little-endian machines.
LE: |56|34|12|00|
(1) |L |H |B |
| W |
(2) |L |H |B |
| W |
On big-endian:
BE: |00|12|34|56|
(1) |L |H |B |
| W |
(2) | |B |H |L |
| W |
Hence, (1) is, strictly speaking, undefined. Should you cast it to
char* and then do the pointer arithmetic, you should get better
results. I've only had to use reinterpret_cast<uintN_t*, for
N=16,...> when calling htobeN* with a pointer to _char_.
Hence, (2) is implementation-defined, specifically with respect to
byte ordering.
I suggest unions with packed structs if you want to be more explicit:
union u32_u8 {
uint32_t DW;
struct {
union {
struct {
uint8_t L;
uint8_t H;
};
uint16_t W;
};
uint8_t B;
};
};
Then
u32_u8& example = *reinterpret_cast<u32_u8*>(dword);
Or, better yet,
u32_u8 example2; example2.DW = dword;
// proceed as usual, using members of example2
Also,
extern "C" {
#include <stdint.h>
}
, if supported.
For microsoft, look up ms-inttypes and save the headers to your system
include directory.
Then you can use (u)intN_t instead of __intN.
C++0x should have <cstdint>
* BSD. See <endian.h>.
Remember, assumptions + reinterpret_cast = UB.
I've only had to use reinterpret_cast when serializing/deserializing
multibyte integers, for e.g. disk and cross-thread/process exception
passing via pipes, and when doing casts _which violate conversion
rules_, e.g. from void * to function pointer (this was an experiment
in using a std::map<std::string, const void*> to implement runtime-
based named parameters),