In C the result of an expression depends on the types of the operands (or some of the operands). Particularly, 1
is an int
(signed), therefore 1 << n
is also int
.
The type (including signed-ness) of 0x80000000
is determined by the rules here and it depends on the size of int
and other integer types on your system, which you haven't specified. A type is chosen such that 0x80000000
(a large positive number) is in range for that type.
In case you have any misconception: the literal 0x80000000
is a large positive number. People sometimes mistakenly equate it to a negative number, mixing up values with representations.
In your question you say "Why is 0x80000000 is treated as unsigned?". However your code does not actually rely on the signed-ness of 0x80000000
. The only thing you do with it is pass it to the function which takes unsigned long
parameter. So whether or not it is signed or unsigned doesn't matter; when passed to the conversion it is converted to an unsigned long
with the same value. (Since 0x80000000
is within the minimum guaranteed range for unsigned long
there is no chance of it being out of range).
So, that's 0x80000000
dealt with. What about 1 << 31
? If your system has 32-bit int (or narrower) this causes undefined behaviour due to signed arithmetic overflow. (Link to further reading). If your system has larger ints then this will produce the same output as the 0x80000000
line.
If you use 1u << 31
instead, and you have 32-bit ints, then there is no undefined behaviour and you are guaranteed to see the program output 80000000
twice.
Since your output was not 80000000
then we can conclude that your system has 32-bit (or narrower) int, and your program actually causes undefined behaviour. The type of 0x80000000
would be unsigned int
if int
is 32-bit, or unsigned long
otherwise.