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

Time:01-02

I get a "shift count >= width of type" warning for test1 but I do not get this same warning for test3. All tests appear to work and casting gets rid of the warning. However, I don't understand why I don't get a similar warning for test3.

test1 and test2 are 64 bit long type on my machine and test3 and test4 are 32 bit int type on my machine. I use similar code to this to pack/unpack data to send over a network. I've rewrote it to fit this question.

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);

CodePudding user response:

There's no warning in case 3 due to integer promotions.

Generally speaking, when a type smaller than int is used in an expression it is first promoted to int. For example, when you do this:

arr2[0] << 24

The value arr2[0] has type unsigned char, but this value is first promoted to int, then the promoted value is shifted. So case 3 and case 4 are actually the same.

One thing to watch for here is that due to the promotion to a signed type, you could be shifting a bit with value 1 into the sign bit. Doing so triggers undefined behavior.

The safest way to handle this is to cast each value to an unsigned type of the appropriate size before shifting, and the result should be assigned to the proper unsigned type. Also note the macros for size-specific printf format specifiers.

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);
  • Related