shift count >= width of type warning for 64 bit but not 32 位

shift count >= width of type warning for 64 bit but not 32 bit

我收到 test1 的“移位计数 >= 类型宽度”警告,但我没有收到 test3 的相同警告。所有测试似乎都有效,并且转换消除了警告。但是,我不明白为什么我没有收到类似的 test3 警告。

test1 和 test2 在我的机器上是 64 位 long 类型,test3 和 test4 在我的机器上是 32 位 int 类型。我使用与此类似的代码来 pack/unpack 数据通过网络发送。我重写了它以适应这个问题。

unsigned char arr1[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned char arr2[4] = {0xFF, 0xFF, 0xFF, 0xFF};
long test1;
long test2;
int test3;
int test4;

// Gives warning: shift count >= width of type
test1 = (arr1[0] << 56) | (arr1[1] << 48) | (arr1[2] << 40) | (arr1[3] << 32)
    | (arr1[4] << 24) | (arr1[5] << 16) | (arr1[6] << 8) | arr1[7];
printf("Test 1: %lX\n", test1);

// No warning
test2 = ((long)arr1[0] << 56) | ((long)arr1[1] << 48) | ((long)arr1[2] << 40)
    | ((long)arr1[3] << 32) | ((long)arr1[4] << 24) | ((long)arr1[5] << 16)
    | ((long)arr1[6] << 8) | (long)arr1[7];
printf("Test 2: %lX\n", test2);

// No warning. Shouldn't this give a warning?
test3 = (arr2[0] << 24) | (arr2[1] << 16) | (arr2[2] << 8) | arr2[3];
printf("Test 3: %X\n", test3);

// No warning
test4 = ((int)arr2[0] << 24) | ((int)arr2[1] << 16) | ((int)arr2[2] << 8)
    | (int)arr2[3];
printf("Test 4: %X\n", test4);

由于整数提升,案例 3 没有警告。

一般来说,当在表达式中使用小于 int 的类型时,它首先被提升为 int。例如,当您这样做时:

arr2[0] << 24

arr2[0] 的类型为 unsigned char,但此值首先被提升为 int,然后被提升的值被移位。所以case 3和case 4其实是一样的

这里要注意的一件事是,由于升级为有符号类型,您可能会将值为 1 的位移入符号位。这样做会触发 undefined behavior.

处理这个问题最安全的方法是在移位之前将每个值转换为适当大小的无符号类型,并且结果应该分配给适当的无符号类型。另请注意特定于大小的 printf 格式说明符的宏。

unsigned char arr1[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned char arr2[4] = {0xFF, 0xFF, 0xFF, 0xFF};
uint64_t test2;
uint32_t test4;

test2 = ((uint64_t)arr1[0] << 56) | ((uint64_t)arr1[1] << 48)
      | ((uint64_t)arr1[2] << 40) | ((uint64_t)arr1[3] << 32)
      | ((uint64_t)arr1[4] << 24) | ((uint64_t)arr1[5] << 16)
      | ((uint64_t)arr1[6] << 8)  | (uint64_t)arr1[7];
printf("Test 2: %" PRIX64 "\n", test2);

test4 = ((uint32_t)arr2[0] << 24) | ((uint32_t)arr2[1] << 16) 
      | ((uint32_t)arr2[2] << 8)  | (uint32_t)arr2[3];
printf("Test 4: %" PRIX32 "\n", test4);