I tried left-shifting a unsigned int by 24 like below,
__u8 buf;
__u32 u32_result = 0;
__u64 u64_result = 0;
buf=0xFF;
u32_result= (buf<<24);
u64_result= (buf<<24);
printf("sizeof(__u64):%lu, sizeof(__u32):%lu, sizeof(__u8):%lu,\n", sizeof(__u64), sizeof(__u32), sizeof(__u8));
printf("u32_result: %u MB\n", u32_result);
printf("u64_result: %llu MB\n", u64_result);
The execute result as below,
sizeof(__u64):8, sizeof(__u32):4, sizeof(__u8):1,
u32_result: 4278190080 MB
u64_result: 18446744073692774400 MB
And, I'm confusing about why the __u32 and __u64 have different result? Is anyone can help me to figure-out? Thank a lot!
CodePudding user response:
buf<<24
is promoted to an int
, as is usual for operations on small types (besides, 24 is an int
). Specifically, this is a signed int
, not unsigned
.
Since unsigned arithmetic wraps around, the point where it wraps around matters, and that's of course much higher for your __u64
.
CodePudding user response:
buf is converted to int then left shifted which gives a negative 32 bit integer. Assigned to 64 but it is first converted to a huge negative integer which becomes a huge 64 bit integer.
Safest is ((uint64_t) buf) << 24. BTW uint64_t is a standard C and C type, so the reader knows what’s going on unlike with your non-standard types.
CodePudding user response:
You are probably working on a system with 32bit int
. What happens here is, that in the expression buf << 24
both operands undergo integer promotion first and then the type of the result is inferred (You can read up the exact rules for the shift operator here and for the promotions here). Because buf
is of type __u8
which is smaller than int
it is promoted to int
(the signed version). Shifting that right 24 bits is undefined behavior, because the result is not representable in the promoted type of lhs:
For signed lhs with nonnegative values, the value of
LHS << RHS
isLHS * 2 ^ RHS
if it is representable in the promoted type of lhs, otherwise the behavior is undefined.
Tldr: Your left hand side operand must have a type that is wide enough to store the result of the shift. You can achieve this e.g. by casting it to __u32
:
(__u32)buf << 24