With the help of the awesome people on SO, I think I can answer my own question now:
Just to correct the notion that 0x80000000
can't fit in an int:
It is possible to store, without loss or undefined behavior, the value 0x80000000
to an int
(assuming sizeof(int) == 4
). The following code can demonstrate this behavior:
#include <limits.h>
#include <stdio.h>
int main() {
int i = INT_MIN;
printf("%X\n", i);
return 0;
}
Assigning the literal 0x80000000
to a variable is little more nuanced, though.
What the other others failed to mention (except @Daniel Langr) is the fact that C++ doesn't have a concept of negative literals.
There are no negative integer literals. Expressions such as -1
apply the unary minus operator to the value represented by the literal, which may involve implicit type conversions.
With this in mind, the literal 0x80000000
is always treated as a positive number. Negations come after the size and sign have been determined. This is important: negations don't affect the unsigned/signedness of the literal, only the base and the value do. 0x80000000
is too big to fit in a signed integer, so C++ tries to use the next applicable type: unsigned int
, which then succeeds. The order of types C++ tries depends on the base of the literal plus any suffixes it may or may not have.
The table is listed here: https://en.cppreference.com/w/cpp/language/integer_literal
So with this rule in mind let's work out some examples:
-2147483648
: Treated as a long int
because it can't fit in an int
.
2147483648
: Treated as a long int
because C++ doesn't consider unsigned int
as a candidate for decimal literals.
0x80000000
: Treated as an unsigned int
because C++ considers unsigned int
as a candidate for non-decimal literals.
(-2147483647 - 1)
: Treated as an int
. This is typically how INT_MIN
is defined to preserve the type of the literal as an int
. This is the type safe way of saying -2147483648
as an int
.
-0x80000000
: Treated as an unsigned int
even though there's a negation. Negating any unsigned
is undefined behavior, though.
-0x80000000l
: Treated as a long int
and the sign is properly negated.