2 things:
char
can be signed or unsigned, hence shouldn't be used storing for data types other than characters.
In C, C++ and most C-like languages, any types narrower than int
must be promoted to int
in an expression, and your statement will be treated like this
unsigned long ret = ((int)b[0] << 56) | ((int)b[1] << 48)
| ((int)b[2] << 40) | ((int)b[3] << 32)
| ((int)b[4] << 24) | ((int)b[5] << 16)
| ((int)b[6] << 8) | ((int)b[7]);
If char
is signed, it'll be promoted to int
using sign extension. As a result the top bits will be filled with 1s if the byte value is negative.
In MSVC char
is signed by default. You can use /J
to make char unsigned, which will solve part of your problem. But then another problem arises:
In Windows long
is a 32-bit type, consequently you can't pack 8 bytes into it. Moreover int
is also 32-bit on most modern systems, and after promoting b[i]
to int shifting more than 31 is undefined behavior which is what your program does.
So to fix all the problems portably you need to:
- Cast all
b[i]
to unsigned char
or uint8_t
, or masking the high bits by ANDing it with 0xFF like 0605002 suggested. Or simply change the type of b
to unsigned char&*
instead of char&*
- Change the return type to a type at least 64-bit wide like
(unsigned) long long
, (u)int64_t
or (u)int_least64_t
The result may look like this
uint64_t readAndSkipLong(unsigned char*& b)
{
return ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48)
| ((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32)
| ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16)
| ((uint64_t)b[6] << 8) | ((uint64_t)b[7]);
}
or
uint64_t readAndSkipLong(char*& b)
{
return ((uint64_t)(uint8_t)b[0] << 56) | ((uint64_t)(uint8_t)b[1] << 48)
| ((uint64_t)(uint8_t)b[2] << 40) | ((uint64_t)(uint8_t)b[3] << 32)
| ((uint64_t)(uint8_t)b[4] << 24) | ((uint64_t)(uint8_t)b[5] << 16)
| ((uint64_t)(uint8_t)b[6] << 8) | ((uint64_t)(uint8_t)b[7]);
}
However you don't actually need to write a function to reverse endian. There are already ntohll()
and htonll()
for that purpose
reversedEndian = ntohll(originalValue);
If the input must be a char
array then just copy the value to a uint64_t
memcpy(&originalValue, &b, sizeof originalValue);
reversedEndian = ntohll(originalValue);
You can further reduce the whole thing to reversedEndian = ntohll(*(int64_t*)&b);
if strict aliasing is allowed because on x86 unaligned access is generally permitted