On my laptop, running the following code:
#include <iostream>
using namespace std;
int main()
{
char a;
cout << sizeof(~a) << endl;
}
prints 4
.
I expected the result of ~a
to be a char
, but apparently, it is an int
.
Why is that?
On my laptop, running the following code:
#include <iostream>
using namespace std;
int main()
{
char a;
cout << sizeof(~a) << endl;
}
prints 4
.
I expected the result of ~a
to be a char
, but apparently, it is an int
.
Why is that?
~
is an arithemtic operator (bitwise NOT), and a
is being promoted from signed char
to int
(and in many implementations sizeof(int) == 4
). See below for an explanation:
http://en.cppreference.com/w/cpp/language/implicit_conversion#integral_promotion
Prvalues of small integral types (such as char) may be converted to prvalues of larger integral types (such as int). In particular, arithmetic operators do not accept types smaller than int as arguments, and integral promotions are automatically applied after lvalue-to-rvalue conversion, if applicable. This conversion always preserves the value.
The standard says (§[expr.primary]/10):
The operand of ~ shall have integral or unscoped enumeration type; the result is the one’s complement of its operand. Integral promotions are performed. The type of the result is the type of the promoted operand.
"Integral promotions" means (§[conv.prom]/1):
A prvalue of an integer type other than
bool
,char16_t
,char32_t
, orwchar_t
whose integer conversion rank (4.13) is less than the rank ofint
can be converted to a prvalue of typeint
ifint
can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of typeunsigned int
.
In your case, a
has type char
, which has a conversion rank less than the rank of int
1, so it's being promoted to either int
or unsigned int
, both of which have the same size (apparently 4
in your implementation).
As to why things were done this way: I think a great deal is that it just simplifies both the language definition and the compiler quite a bit. Rather than having to generate code separately for nearly every type, it does its best to collapse everything down to a few types, and most code is generated only for those types. That's not so much the case any more (now that we have multiple types larger than int
), but back when C was young, the integer types were: char
, short
, int
(and unsigned versions of those), so all the other types were promoted to int
, and all code to manipulate anything was done with int
s.
Note that this applied to function calls and such too: in early versions of C there were no function prototypes, so any parameter of type char
or short
was promoted to int
before being passed to a function too.
The same basic idea was followed with floating point types: under most circumstances (including passing them to functions) float
s were promoted to double
, and all the actual processing was done on double
s (after which you could convert back to float
, if necessary.
1.3 A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.
[...]
1.6 The rank of char shall equal the rank of signed char and unsigned char.