我正在阅读关于按位运算符的章节,我遇到了1的补码运算符程序,并决定在Visual C ++上运行它。
int main ()
{
unsigned char c = 4, d;
d = ~c;
printf("%d\n", d);
}
它提供有效输出:251
然后我决定直接打印d
的值,而不是使用~c
作为变量来保存~c
的值。
int main ()
{
unsigned char c=4;
printf("%d\n", ~c);
}
它提供输出-5
。
为什么它不起作用?
答案 0 :(得分:55)
在此声明中:
printf("%d",~c);
在应用 c
(按位补码)运算符之前,int
转换为~
1 类型。这是因为整数提升,它们被调用到~
的操作数。在这种情况下,unsigned char
类型的对象会被提升为(已签名)int
,然后~
运算符评估后printf
运算符使用{}}匹配%d
1}}格式说明符。
请注意,默认参数提升(因为printf
是一个可变函数)在这里不起任何作用,因为对象已经是int
类型。
另一方面,在此代码中:
unsigned char c = 4, d;
d = ~c;
printf("%d", d);
发生以下步骤:
c
(以同样的方式,如上所述)~
是整数促销的主题~c
rvalue被评估为(已签名)int
值(例如-5
)d=~c
进行从int
到unsigned char
的隐式转换,因为d
具有此类型。您可能会认为它与d = (unsigned char) ~c
相同。请注意,d
不能为负(这是所有无符号类型的一般规则)。printf("%d", d);
调用默认参数提升,因此d
转换为int
并保留(非负)值(即int
type可以表示unsigned char
类型的所有值。 1)假设int
可以代表unsigned char
的所有值(请参阅下面的TC comment),但它非常可能以这种方式发生。更具体地说,我们假设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)。
答案 1 :(得分:27)
char
在第二个代码段中的操作int
之前的printf
语句中被提升为~
。所以c
,这是
0000 0100 (2's complement)
二进制文件中的被提升为(假设是32位机器)
0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x
及其逐位补码等于值的二进制补码减一(~x = −x − 1
)
1111 1111 1111 1111 1111 1111 1111 1011
以二进制补码形式的十进制-5
。
请注意,char
c
到int
的默认促销也会在
d = ~c;
在补语操作之前,但结果将转换回unsigned char
,d
的类型为unsigned char
。
在简单赋值(
=
)中,右操作数的值将转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。
和
赋值表达式的类型是左操作数将具有的类型 在左值转换后。
答案 2 :(得分:17)
要理解代码的行为,您需要学习名为'Integer Promotions'的概念(在unsigned char
操作数上按位操作之前隐式地在代码中发生)如N1570委员会草案中所述:
§ 6.5.3.3 Unary arithmetic operators
- 醇>
~
运算符的结果是它的按位补码 (提升)操作数(也就是说,结果中的每个位都是设置的 如果未设置转换后的操作数中的相应位)。的的 对操作数执行整数提升,结果为 提升类型。如果推广类型是" '无符号类型', 表达式~E
等效于其中可表示的最大值 类型减去E
"。
因为unsigned char
类型比(因为它需要更少的字节)int
类型更窄,所以 - 抽象机器(编译器)执行的隐式类型提升和变量c
的值被提升为编译时int
(在应用补充操作~
之前)。它是正确执行程序所必需的,因为~
需要一个整数操作数。
§ 6.5 Expressions
- 一些运算符(一元运算符
醇>~
,和二元运算符<<
,>>
,&
,^
,和|
, 统称为按位运算符)需要具有的操作数 整数类型。这些运算符产生的值取决于内部表示 整数,并且已签名类型具有实现定义和未定义的方面。
编译器非常智能 - 足以分析表达式,检查表达式的语义,执行类型检查和算术转换(如果需要)。这就是在~
类型上应用char
的原因,我们不需要明确地编写~(int)c
- 称为显式类型转换(并避免错误)。< / p>
注意:
c
的值已升级为int
~c
,但c
的类型仍为unsigned char
- 其类型不是~
。不要困惑。
重要提示: int
操作的结果属于#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;
}
类型!,请检查以下代码(我没有vs-compiler,我是使用gcc):
$ gcc -std=gnu99 -Wall -pedantic x.c -o x
$ ./x
sizeof(int) = 4,
sizeof(unsigned char) = 1
sizeof(~c) = 4
编译它,然后运行:
~c
注意:int
的结果大小与unsigned char
相同,但不等于~
- int
运算符的结果这个表达式是-
!如上所述6.5.3.3 Unary arithmetic operators
- 一元
醇>~c
运算符的结果是其(提升的)操作数的否定。整数 促销是在操作数上执行的,结果具有提升类型。
现在,正如@haccks在他的answer中解释的那样 - 在32位机器上c = 4
的结果和1111 1111 1111 1111 1111 1111 1111 1011
的值是:
-5
十进制为b = ~c;
- 这是第二个代码的输出!
在第一个代码中,要了解b
,还有一行很有意思,因为unsigned char
是~c
变量,int
的结果属于~c
类型,因此为了适应b
到 1111 1111 1111 1111 1111 1111 1111 1011 // -5 & 0xFF
& 0000 0000 0000 0000 0000 0000 1111 1111 // - one byte
-------------------------------------------
1111 1011
结果值(~c)的结果值,被截断以适合无符号字符类型 as如下:
1111 1011
251
的十进制等值为printf("\n ~c = %d", ~c & 0xFF);
。您可以使用以下方式获得相同的效果:
{{1}}
或@ouah在answer中使用显式强制转换的建议。
答案 3 :(得分:12)
将~
运算符应用于c
时,它会被提升为int
,结果也是int
。
然后
unsigned char
,然后提升为signed int
并打印。signed int
。答案 4 :(得分:10)
它给出了op -5。为什么它不起作用?
而不是:
printf("%d",~c);
使用:
printf("%d", (unsigned char) ~c);
获得与第一个示例相同的结果。
~
操作数进行整数提升,默认参数提升应用于可变参数函数的参数。
答案 5 :(得分:8)
整数推广,来自标准:
如果带有符号整数类型的操作数的类型可以表示全部 具有无符号整数类型的操作数类型的值, 具有无符号整数类型的操作数应转换为该类型 带有符号整数类型的操作数。