为什么是printf("%d",~a); 当a等于3时显示-4?

Fif*_*ifi 3 c printf unsigned char negation

运行以下程序显示-4,而预期252:

unsigned char a=3;
printf ("%d", ~a);
Run Code Online (Sandbox Code Playgroud)

为什么这段代码不显示252?

我还根据提出的答案测试了下面的内容:

printf ("%u", ~a);
Run Code Online (Sandbox Code Playgroud)

显示:4294967292

printf ("%hu", ~a);
Run Code Online (Sandbox Code Playgroud)

显示:65532

为什么~a不返回unsigned char,因为一个一个unsigned char

我的问题不是我应该怎么做才能显示252?我的问题是为什么没有显示252?

Swo*_*ish 9

除了@Someprogrammerdude的答案之外,这里还有来自The Book 1)的相关段落:

一元算术运算符(§6.5.3.3/ 4)

运算符的结果是其(提升的 [ !! ] )操作数的按位补码(也就是说,当且仅当未设置转换后的操作数中的相应位时,才会设置结果中的每个位).整数提升在操作数上执行,结果具有提升类型.如果提升的类型是无符号类型,表达式〜戊是等同于在该类型减去表示的最大值ê.  

算术操作数 - 布尔,字符和整数(§6.3.1.1):

  1. 每个整数类型都有一个整数转换等级,定义如下:

    • 没有两个有符号整数类型具有相同的等级,即使它们具有相同的表示.
    • 有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级.
    • 军衔长长整型应大于军衔更高的长整型,这应该比军衔更高的INT,这应该比军衔更高的短整型,这应该比军衔更高的符号字符.
    • 任何无符号整数类型的等级应等于相应的有符号整数类型的等级(如果有的话).
    • 任何标准整数类型的等级应大于具有相同宽度的任何扩展整数类型的等级.
    • 军衔应等于军衔符号的字符无符号的字符.
    • _Bool的等级应小于所有其他标准整数类型的等级.
    • 任何枚举类型的等级应等于兼容整数类型的等级(见6.7.2.2).
    • 任何扩展有符号整数类型相对于具有相同精度的另一个扩展有符号整数类型的等级是实现定义的,但仍然受制于确定整数转换等级的其他规则.
    • 对于所有整数类型T1,T2T3,如果T1具有比T2更大的秩并且T2具有比T3更大的秩,则T1具有比T3更大的秩.
  2. 如果可以使用intunsigned int,则可以在表达式中使用以下内容:

    • 具有整数类型的对象或表达式,其整数转换等级小于或等于intunsigned int的等级.
    • _Bool,int,signed intunsigned int类型的位字段.如果int可以表示原始类型的所有值,则该值将转换为int ; 否则,它将转换为unsigned int.这些被称为整数促销.48)所有其他类型的整数提升不变.
  3. 整数促销保留包括符号在内的值.如前所述,"普通" 字符是否被视为已签名是实现定义的.

48)整数提升仅适用于:通常的算术转换,某些参数表达式,一元+,-运算符的操作数,以及移位运算符的两个操作数的一部分,由它们各自指定小节.


你的问题:

为什么~a不返回unsigned char,因为aunsigned char

因为整数促销适用.

unsigned char a = 3;
printf ("%d", ~a);
Run Code Online (Sandbox Code Playgroud)

a是一个unsigned char范围可以用a表示的类型int.所以a升职为int.假设32位宽ints和2补码:

  3 10 = 0000 0000 0000 0000 0000 0000 0000 00112
~3 10 = 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

0100 2 = 0×2 3 + 1×2 2 + 0×2 2 + 0×2 2
           1 ×2 2
            =    4 10
            = -4 10(带原始符号)

~~> 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 
Run Code Online (Sandbox Code Playgroud)

*)感谢chux让我记得那char可能signed!施放到(可能signed)char会给出-4的错误结果.

要在没有强制转换的情况下获得相同的结果,可以使用长度修饰符hh:

fprintf函数(§7.19.6.1/ 7)

长度修饰符及其含义为:

hh   指定以下d,i,o,u,xX转换说明符适用于signed charunsigned char参数(该参数将根据整数提升进行提升,但是在打印之前,其值应转换为有符号字符或未签名字符 ;); 或者后面的n转换说明符适用于指向signed char参数的指针.

[...]

unsigned char a = 3;
printf("%hhu\n", ~a);  // prints 252
Run Code Online (Sandbox Code Playgroud)


您的其他尝试的问题:

printf ("%u", ~a);
Run Code Online (Sandbox Code Playgroud)

显示:4294967292

printf ("%hu", ~a);
Run Code Online (Sandbox Code Playgroud)

显示:65532

由于~aint埃格尔,它不是正确类型的格式说明u

fprintf函数(第7.19.6.1/9节):

如果转换规范无效,则行为未定义.248) 如果任何参数不是相应转换规范的正确类型,则行为未定义.



1)ISO/IEC 9899/Cor3:2007又名C99:TR3又名C99