为什么补码通过printf表现不同?

San*_*sj5 57 c variables types bitwise-operators unsigned-char

我正在阅读关于按位运算符的章节,我遇到了1的补码运算符程序,并决定在Visual C++上运行它.

int main ()
{
   unsigned char c = 4, d;
   d = ~c;
   printf("%d\n", d);
}
Run Code Online (Sandbox Code Playgroud)

它给出了有效的输出: 251

然后我决定直接打印值,而不是d用作变量来保存~c~c.

int main ()
{
   unsigned char c=4;
   printf("%d\n", ~c);
}
Run Code Online (Sandbox Code Playgroud)

它给出了输出-5.

它为什么不起作用?

Grz*_*ski 55

在这个声明中:

printf("%d",~c);
Run Code Online (Sandbox Code Playgroud)

在应用(按位补码)运算符之前c其转换为int1类型.这是因为整数提升,被调用到的操作数.在这种情况下,类型的对象被提升为(带符号),然后(在运算符评估之后)由函数使用,具有匹配的格式说明符. ~~unsigned charint~printf%d

请注意,默认参数提升(作为printf可变参数函数)在此处不起任何作用,因为对象已经是类型int.

另一方面,在这段代码中:

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

发生以下步骤:

  • c因为(以同样的方式,如上所述)是整数促销的主题~
  • ~crvalue被评估为(有符号)int值(例如-5)
  • d=~c使得从隐式转换intunsigned char,因为d具有这样的类型.你可能会认为它和它一样d = (unsigned char) ~c.请注意,d不能为负(这是所有无符号类型的一般规则).
  • printf("%d", d);调用默认参数提升,因此d转换为int并保留(非负)值(即int类型可以表示所有unsigned char类型的值).

1)假设它int可以代表所有的值unsigned char(参见下面的TC 评论),但可能以这种方式发生.更具体地说,我们假设INT_MAX >= UCHAR_MAX持有.通常,sizeof(int) > sizeof(unsigned char)保持和字节由8位组成.否则c将转换为unsigned int(如C11子条款§6.3.1.1/ p2),格式说明符也应相应更改,%u以避免获得UB(C11§7.21.6.1/ p9).

  • 但是这也发生在`d = ~c`中.那么真正的差异就是在`d = ~c`中转换(返回)到'unsigend char`,而不是在'printf`调用中. (9认同)

hac*_*cks 27

char被提升到intprintf操作之前的语句~排在第二片段.那么c,这是

0000 0100 (2's complement)  
Run Code Online (Sandbox Code Playgroud)

在二进制中被提升为(假设32位机器)

0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x  
Run Code Online (Sandbox Code Playgroud)

并且它的逐位补码等于值的二进制补码减去一(~x = ?x ? 1)

1111 1111 1111 1111 1111 1111 1111 1011  
Run Code Online (Sandbox Code Playgroud)

这是-5二进制补码形式的十进制数.

请注意,也会执行 char c to 的默认促销int

d = ~c;
Run Code Online (Sandbox Code Playgroud)

补体操作之前但结果被转换回unsigned chard是类型unsigned char.

C11:6.5.16.1简单分配(p2):

在简单赋值(=)中,右操作数的值将转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值.

6.5.16(p3):

赋值表达式的类型是左值操作数在左值转换后将具有的类型.

  • @Downvoter,acomment将不胜感激. (4认同)

Gri*_*han 17

要理解代码的行为,你需要学习一个名为"整数促销"的概念(在操作unsigned char数上按位操作之前隐式地在代码中发生)如N1570委员会草案所述:

§6.5.3.3一元算术运算符

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

因为unsigned char类型比(因为它需要更少的字节)int类型更窄,所以- 抽象机器(编译器)执行的隐式类型提升和变量的值在编译时(在应用补充操作之前)c被提升.它是正确执行程序所必需的,因为需要一个整数操作数.int~~

§6.5表达式

  1. 一些运营商(一元运算符~,和二进制运算符<<,>>,&,^,和|,统称为位运算符)都要求有具有整型操作数.这些运算符产生的值取决于整数的内部表示,并且具有已签名类型的实现定义和未定义方面.

编译器足够智能 - 足以分析表达式,检查表达式的语义,执行类型检查和算术转换(如果需要).这就是应用~on char类型的原因,我们不需要显式写~(int)c- 称为显式类型转换(并避免错误).

注意:

  1. c被提升到int表达式中~c,但类型c仍然是unsigned char- 它的类型不是.不要混淆.

  2. 重要:~操作的结果是int类型!,检查下面的代码(我没有vs-compiler,我使用的是gcc):

    #include<stdio.h>
    #include<stdlib.h>
    int main(void){
       unsigned char c = 4;
       printf(" sizeof(int) = %zu,\n sizeof(unsigned char) = %zu",
                sizeof(int),
                sizeof(unsigned char));
       printf("\n sizeof(~c) = %zu", sizeof(~c));        
       printf("\n");
       return EXIT_SUCCESS;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    编译它,然后运行:

    $ gcc -std=gnu99 -Wall -pedantic x.c -o x
    $ ./x
    sizeof(int) = 4,
    sizeof(unsigned char) = 1
    sizeof(~c) = 4
    
    Run Code Online (Sandbox Code Playgroud)

    注意:结果的大小~c 相同int,但不等于unsigned char- ~表达式中运算符的结果是int!如上所述6.5.3.3一元算术运算符

    1. 一元运算-符的结果是其(提升的)操作数的否定.整数提升在操作数上执行,结果具有提升类型.

现在,正如@haccks在他的回答中解释的那样- ~c32位机器的结果和价值c = 4是:

1111 1111 1111 1111 1111 1111 1111 1011
Run Code Online (Sandbox Code Playgroud)

十进制是-5- 这是你的第二个代码的输出!

第一码,一个多线有趣的是理解b = ~c;,因为bunsigned char可变的,并且结果~c就是的 int类型,所以,以适应的结果的值~c,以b产生值(〜c)的截短以适应unsigned char类型 如下:

    1111 1111 1111 1111 1111 1111 1111 1011  // -5 & 0xFF
 &  0000 0000 0000 0000 0000 0000 1111 1111  // - one byte      
    -------------------------------------------          
                                  1111 1011  
Run Code Online (Sandbox Code Playgroud)

十进制当量1111 1011251.使用以下方法可以获得相同的效

printf("\n ~c = %d", ~c  & 0xFF); 
Run Code Online (Sandbox Code Playgroud)

或者@ouah在他的回答中使用显式强制转换的建议.

  • + int(PI/3)用于实际引用相关规范部分......没有其他答案实际解释*为什么*促销发生在这里!this->`一些运算符(一元运算符〜,二元运算符<<,>>,&,^和|,统称为按位运算符)需要具有整数类型的操作数.今天我的日子. (2认同)

alk*_*alk 12

当应用~运算符到c它被提升时int,结果也是int如此.

然后

  • 在第一个例子中,结果被转换为unsigned char然后被提升signed int并打印出来.
  • 在第二个例子中,结果打印为signed int.


oua*_*uah 10

它给出了op -5.为什么它不起作用?

代替:

printf("%d",~c);
Run Code Online (Sandbox Code Playgroud)

使用:

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

获得与第一个示例中相同的结果.

~ 操作数经过整数提升,默认参数提升应用于可变函数的参数.


Dav*_*eri 8

整数推广,来自标准:

如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数应转换为带有符号整数类型的操作数的类型.