Прежде всего, чтобы назвать тип шириной ровно 32 бита, используйте uint32_t
, а не unsigned long int
. unsigned long int
обычно имеет ширину 64 бита в 64-битных * никсах (так называемых LP64), тогда как в Windows это 32 бита (LLP64).
Так или иначе, проблема в целочисленных акциях . Операнд в арифметической операции с рангом преобразования меньше int
или unsigned int
будет конвертирован в int
или unsigned int
, в зависимости от того, в какой диапазон он входит. Поскольку все unsigned char
s представимы как signed int
s, pdest[3]
преобразуется в signed int
, а результат pdest[3] << 24
также имеет тип signed int
!. Теперь, если для него установлен самый значимый бит, этот бит смещается в знаковый бит целого числа, и его поведение соответствует стандарту C undefined .
Однако GCC определил поведение для этого случая; там результат - просто отрицательное целое число с представлением дополнения 2; следовательно, результат (unsigned char)0xFF << 24
равен (int)-16777216
. Теперь для операции |
ее необходимо повысить до ранга другого операнда , который равен unsigned
. Преобразование без знака происходит, как будто путем многократного сложения или вычитания на единицу больше, чем максимум (то есть многократного сложения или вычитания 2⁶⁴) до тех пор, пока значение не попадет в диапазон значения. Поскольку unsigned long
на вашей платформе составляет 64 бита, результатом этого преобразования будет 2 ^ 64 - 16777216 или 18446744073692774400, что равно ORred с битами из предыдущих шагов.
как исправить? Легко, непосредственно перед сменой, приведите каждое смещенное число к uint32_t
. Печать с помощью макроса PRIu32
:
#include <inttypes.h>
...
uint32_t l1=0;
l1 |= (uint32_t)pdest[0];
l1 |= (uint32_t)pdest[1] << 8;
l1 |= (uint32_t)pdest[2] << 16;
l1 |= (uint32_t)pdest[3] << 24;
printf ("%" PRIu32, l1);