首页 > 解决方案 > 为什么 printf ("%d", ~a); 当 a 等于 3 时显示 -4?

问题描述

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

unsigned char a=3;
printf ("%d", ~a);

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

我还根据建议的答案测试了以下内容:

printf ("%u", ~a);

显示:4294967292

printf ("%hu", ~a);

显示:65532

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

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

标签: ccharprintfunsignednegation

解决方案


除了@Someprogrammerdude 的回答,以下是 The Book 1)中的相关段落:

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

~运算符的结果是其(提升的 [ !! ] )操作数的按位补码(即,当且仅当未设置转换操作数中的相应位时,结果中的每个位都被设置)。整数提升在操作数上执行,结果具有提升的类型。如果提升的类型是无符号类型,则表达式~E等价于该类型中可表示的最大值减去E  

算术操作数 - 布尔值、字符和整数(第 6.3.1.1 节)

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

    • 任何两个有符号整数类型都不应具有相同的等级,即使它们具有相同的表示。
    • 有符号整数类型的秩应大于任何精度较低的有符号整数类型的秩。
    • long long int的等级要大于 long int 的等级,long int的等级要大于int的等级,要大于short int的等级,要大于signed char的等级。
    • 任何无符号整数类型的等级应等于相应的有符号整数类型的等级,如果有的话。
    • 任何标准整数类型的秩都应大于任何具有相同宽度的扩展整数类型的秩。
    • char的等级应等于signed charunsigned char的等级。
    • _Bool的等级应小于所有其他标准整数类型的等级。
    • 任何枚举类型的等级应等于兼容整数类型的等级(见 6.7.2.2)。
    • 任何扩展有符号整数类型相对于另一个具有相同精度的扩展有符号整数类型的等级是实现定义的,但仍受用于确定整数转换等级的其他规则的约束。
    • 对于所有整数类型T1T2T3,如果T1的秩大于T2T2的秩大于T3,则T1的秩大于T3
  2. 可以在可以使用intunsigned int的表达式中使用以下内容:

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

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


你的问题:

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

因为整数促销适用。

unsigned char a = 3;
printf ("%d", ~a);

a是一个unsigned char,一个范围可以用一个来表示的类型int。所以a晋升为int. 假设 32 位宽ints 和二进制补码

  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 

*)感谢 chux 让我记得这char可能是signed!强制转换为 (可能signed)char会给出错误的结果 -4。

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

fprintf 函数 (§7.19.6.1/7)

长度修饰符及其含义是:

hh  指定后面的diouxX转换说明符适用于有符号字符无符号字符参数(该参数将根据整数提升进行提升,但其值应在打印前转换为有符号字符无符号字符);或者后面的n转换说明符适用于指向有符号字符参数的指针。

[...]

unsigned char a = 3;
printf("%hhu\n", ~a);  // prints 252


您其他尝试的问题:

printf ("%u", ~a);

显示:4294967292

printf ("%hu", ~a);

显示:65532

由于~a是一个integer,它不是格式说明符的正确类型,u并且

fprintf 函数(§7.19.6.1/9)

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



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


推荐阅读