Assuming unsigned char for byte, this should work portably and safely:
long l = 0;
for (size_t ix=5; ix>1; ++ix) {
l <<= 8;
l |= a[ix];
}
If byte is signed, stick in a cast to unsigned char.
Sorry to be pedantic, but it assumes 8 bits for char. Replace 8 with
CHAR_BIT from <limits.h>.
Good point, but actually depends on the input. If the input is octets
(which is quite likely, most of the world talks octets, 8 is correct.
Also to be extremely pedantic I don't think that the standard gives any
guarantees and to the 'mathematical' behaviour of << when the left argument
is long (unsigned long would be different). So there is no guarantee that l
<<= 8 is the same as l *= 256. But if you were to replace l <<= 8 with l *=
256 then you would run into undefined behaviour because of the potential
overflow (again it would be different with unsigned long). At least that's
how I remember it, as usual I can't find the reference in the standard.
Right.
5.8 clause two. A left shift shifts left the bit pattern making up the
object. The reference to bitpattern makes it clear that this is
implementation defined for signed. It even explicitly states that it has
defined behaviour for unsigned integers.
5 clause 5. All overflows result in UB. So if the shift-left or the *256
results in a (signed) long that would be greater than LONG_MAX, we enter
UB.
3.9.1 clause 4. All unsigned integer types shall obey modulo-2 arithmetic.
3.9.1 clause 3. The bitpatterns for positive signed integers are the same
as their unsigned counterpart.
So left-shifting works only with unsigned long, and with signed long when
the input results in a positive number <= LONG_MAX. It is also guarenteed
to work with negative longs if you know for a fact that your machine uses
2's-complement arithmetic, which is a non-portable assumption.
Also, if the original bit pattern originated on the same machine, the
left-shift solution is required to work with signed long, however, in that
case this is musch simpler:
long l;
memcpy(&l, a+2, sizeof(long));
Multiplying with 256 works for unsigned long, and with signed long when
the input results in a positive number <= LONG_MAX. In practice it will
work with 2's-complement, but contrary to left-shifting the standard does
not guarentee that.
Another point to note, is that the standard probably only guarentees the
result if the type byte is an unsigned type. If the type byte is a signed
char for instance:
l |= a[ix];
would convert a[ix] to a signed long, hardly what you want.
l |= static_cast<unsigned char>(a[ix]);
Makes 4.7 clause 2 apply:
"2 If the destination type is unsigned, the resulting value is the least
unsigned integer congruent to the source integer (modulo 2n where n is the
number of bits used to represent the unsigned type). [Note: In a two s
complement representation, this conversion is conceptual and there is no
change in the bit pattern (if there is no truncation). ]"
Which I don't understand, but clearly implies the bit pattern can change
on non-2's-complement machines.
Sometimes it seems to me that the standard makes simple things very
difficult.
Yup. OTOH, this stuff /is/ difficult. Use text formats or network-order if
portability is an issue. If using network order, unly use it for unsigned
types.
M4