Today, I read a code snippet and got confused how line 0 can work.
#define uint8_t unsigned char
#define uint16_t unsigned short
void func(void) {
uint8_t pkt = 123;
uint16_t value_0 = (uint16_t)(pkt << 8); // line 0
uint16_t value_1 = ((uint16_t)pkt) << 8; // line 1
...
}
For line 0: per my understanding, uint16_t value_0 = (uint16_t)(pkt << 8); will be interpreted into below steps by compilers:
- generate a temp variable with type uint8_t to store the result of (pkt << 8), during which I thought the overflow happens and data is completely lost;
- cast the temp variable into uint16_t, which will be all zeros here;
- copy the value of this temp variable to the uint16_t value_1;
For line 1: per my understanding, uint16_t value_1 = ((uint16_t)pkt) << 8; will be interpreted into below steps by compilers:
- generate a temp variable with type uint16_t and store the value of pkt by casting it. In this step, the value is safely saved;
- save bit wise operation and save value copy;
But I tested the line 0 and line 1, they actually behave the same and both correctly. And I also checked the assembly codes: the assembly codes are the same from line 0 and line 1.
func():
sub sp, sp, #16
mov w0, 123
strb w0, [sp, 15]
ldrb w0, [sp, 15]
and w0, w0, 65535
ubfiz w0, w0, 8, 8
strh w0, [sp, 12]
ldrb w0, [sp, 15]
and w0, w0, 65535
ubfiz w0, w0, 8, 8
strh w0, [sp, 10]
nop
add sp, sp, 16
ret
There must be something I missed (maybe knowledge in the Compilers).
Could you please help me why line 0 and line behave the same? and which is preferred?
If possible can you help me refine the question title because I really don't know which knowledge I miss.
Thank you.
Answer this myself: did a new experiment: try to left bit shift 31 bits in the style line 0 and cast it to uint64_t. The printf shows the result is -2147483648, which means there is integer auto promotion during the cast with 32bits. 6.3.1.1 Boolean, characters, and integers
"If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions."
#include <stdio.h>
#define uint8_t unsigned char
#define uint64_t unsigned long long int
int main(void)
{
uint8_t pkt = 123;
uint64_t value_0 = (uint64_t)(pkt << 31); // line 0
uint64_t value_1 = ((uint64_t)pkt) << 31; // line 1
printf("%lld %lld\n", value_0, value_1);
}
Output:
-2147483648 264140488704