Can someone please explain to me why the upper 32 bits of a uint64_t
are set to one in case number #2:
uint64_t ret = 0;
ret = (((uint64_t)0x00000000000000FF) << 24);
printf("#1 [%016llX]\n", ret);
ret = (0x00000000000000FF << 24);
printf("#2 [%016llX]\n", ret);
uint32_t ret2 = 0;
ret2 = (((uint32_t)0x000000FF) << 8);
printf("#3 [%08X]\n", ret2);
ret2 = (0x000000FF << 8);
printf("#4 [%08X]\n", ret2);
#1 [00000000FF000000]
#2 [FFFFFFFFFF000000]
#3 [0000FF00]
#4 [0000FF00]
You'll notice I've given an "equivalent" 32bit version (cases #3 and #4) which doesn't show the same behaviour...
By default integer literals without a suffix will have type int
if they fit in an int
.
The type of the integer constant
The type of the integer constant is the first type in which the value can fit, from the list of types which depends on which numeric base and which
integer-suffix
was used.
- no suffix
- decimal bases:
- int
- long int
- unsigned long int (until C99)
- long long int (since C99)
- binary, octal, or hexadecimal bases:
- int
- unsigned int
- long int
- unsigned long int
- long long int (since C99)
- unsigned long long int (since C99)
- ...
As a result 0x00000000000000FF
will be an int
regardless of how many zeros you put in. You can check that by printing sizeof 0x00000000000000FF
Therefore, 0x00000000000000FF << 24
results in 0xFF000000 which is a negative value1. That'll again be sign extended when casting to uint64_t
, filling the top 32 bits with ones
Casting help, as you can see in (uint64_t)0x00000000000000FF) << 24
, because now the shift operates on the uint64_t
value instead of int
. You can also use a suffix
0x00000000000000FFU << 24
0x00000000000000FFULL << 24
The first line above does the shift in unsigned int
and then do zero extension to cast to uint64_t
. The second one does the operation in unsigned long long
directly
0x000000FF << 8
doesn't expose the same behavior because the result is 0xFF00 which doesn't have the sign bit set, but it will if you do (int16_t)0x000000FF << 8
There are a lot of related and duplicate questions:
1 Technically shifting into the sign bit results in undefined behavior but in your case the compiler has chosen to leave the result the same as when you shift an unsigned value: 0xFFU << 24 = 0xFF000000U
which when converted to signed produces a negative value
See
User contributions licensed under CC BY-SA 3.0