除了the answer of @Someprogrammerdude,以下是The Book的相关段落1):
~ 运算符的结果是它的按位补码
(升级 [ !! ] em> ) 操作数(即结果中的每一位都被设置当且仅
如果未设置转换后的操作数中的相应位)。 该
对操作数执行整数提升,结果有
提升的类型。 如果提升的类型是无符号类型,则
表达式 ~E 等价于可表示的最大值
键入减号E。
-
每个整数类型都有一个整数转换等级,定义如下:
- 任何两个有符号整数类型都不应具有相同的等级,即使它们具有相同的表示形式。
- 有符号整数类型的等级应大于任何精度较低的有符号整数类型的等级。
-
long long int的rank要大于long int的rank,long int的rank要大于int的rank,大于short int的秩,大于signed char的秩。
- 任何无符号整数类型的等级应等于相应的有符号整数类型的等级,如果有的话。
- 任何标准整数类型的秩都应大于任何具有相同宽度的扩展整数类型的秩。
-
char的等级应该等于signed char和unsigned char的等级。
-
_Bool 的等级应小于所有其他标准整数类型的等级。
- 任何枚举类型的等级都应等于兼容整数类型的等级(参见 6.7.2.2)。
- 任何扩展 signed 整数类型相对于另一个具有相同精度的扩展 signed 整数类型的等级是由实现定义的,但仍受制于其他规则来确定整数转换等级。
- 对于所有整数类型 T1、T2 和 T3,如果 T1 的排名高于 T2 和 T2 的排名高于 T3,然后 T1 的排名高于 T3。
-
可以在表达式中使用 int 或 unsigned int 的地方:
- 具有整数类型的对象或表达式,其整数转换等级小于或等于 int 和 unsigned int 的等级。
-
_Bool、int、signed int 或 unsigned int 类型的位字段。 如果一个int可以表示原始类型的所有值,则将该值转换为一个int;否则,它将被转换为 unsigned int。这些被称为整数提升。48)所有其他类型都不受整数提升的影响。
- 整数提升保留值,包括符号。如前所述,“普通”char 是否被视为 signed 是实现定义的。
48) 整数提升仅适用于:作为常用算术转换的一部分,适用于某些参数表达式,适用于一元 +、-的操作数> 和 ~ 运算符,以及移位运算符的两个操作数,由它们各自的子条款指定。
您的问题:
为什么~a 不返回unsigned char,因为a 是unsigned char?
因为整数促销适用。
unsigned char a = 3;
printf ("%d", ~a);
a 是一个unsigned char,它的范围可以用int 表示。所以a 被提升为int。假设 32 位宽 ints 和 two's complement:
310 = 0000 0000 0000 0000 0000 0000 0000 00112
~310 = 1111 1111 1111 1111 1111 1111 1111 11002
解释为 signed int 的结果是否定的,因为设置了最高有效位,即符号位。
转换为十进制:
1111 1111 1111 1111 1111 1111 1111 11002
¬ 0000 0000 0000 0000 0000 0000 0000 00112
+ 0000 0000 0000 0000 0000 0000 0000 00012
────────────────────────────
0000 0000 0000 0000 0000 0000 0000 01002
01002 = 0 × 23 + 1 × 22 + 0 × 22 + 0 × 22
= 1 × 22
= 410
= −410(带有原始符号)
~> printf() 打印 -4。
要使用使用"%d" 作为格式说明符的原始代码获得所需的 252 结果,需要进行一些转换:
unsigned char a = 3;
printf("%d\n", (int)((unsigned char) ~a)); // prints 252
// ^^^ ^^^^^^^^^^^^^
// | cast the result of ~a back to unsigned char *)
// | to discard the bits > CHAR_BIT
// cast the char back to int to agree with the format specifier
*)感谢chux让我记住char可能是signed!转换为(可能是 signed)char 会给出错误的结果 -4。
要在不进行强制转换的情况下获得相同的结果,您可以使用长度修饰符hh:
长度修饰符及其含义是:
hh 指定后面的d、i、 o、u、x 或 X 转换说明符适用于 signed char 或 unsigned char 参数(该参数将根据整数提升进行提升,但其值应在打印前转换为 signed char 或 unsigned char) ;或者后面的 n 转换说明符适用于指向有符号字符参数的指针。
[...]
unsigned char a = 3;
printf("%hhu\n", ~a); // prints 252
你其他尝试的问题:
printf ("%u", ~a);
显示:4294967292
printf ("%hu", ~a);
显示:65532
由于~a 是integer,它不是格式说明符u 的正确类型,并且
如果转换规范无效,则行为未定义。248)如果任何参数不是相应转换规范的正确类型,则行为未定义。
1) ISO/IEC 9899/Cor3:2007 aka C99:TR3 aka C99