要了解您的代码行为,您需要学习称为
'整数提升'的概念(在对无符号字符操作数进行按位NOT操作之前,在您的代码中隐式发生)。如N1570委员会草案所述:
~
运算符的结果是其(提升后的)操作数的按位补码(即,如果转换后的操作数中对应的位未设置,则结果中的每个位都设置)。对操作数执行整数提升,并使结果具有提升类型。如果提升类型是“ 'unsigned type'”,则表达式~E
等效于该类型中可表示的最大值减去E
“。
因为
unsigned char
类型比
int
类型窄(因为它需要更少的字节),所以在编译时(在应用补码操作
~
之前),抽象机器(编译器)执行隐式类型提升,并将变量
c
的值提升为
int
。这是程序正确执行所必需的,因为
~
需要一个整数操作数。
§ 6.5表达式
4.一些运算符(一元运算符
~
和二元运算符
<<
、
>>
、
&
、
^
和
|
,统称为位运算符)需要具有整数类型的操作数。这些运算符产生的值取决于整数的内部表示,并且对于有符号类型具有实现定义和未定义的方面。
编译器足够聪明,能够分析表达式、检查语义、执行必要的类型检查和算术转换。这就是为什么在 char
类型上应用 ~
时,我们不需要显式地编写 ~(int)c
—— 这被称为显式类型转换(并且避免错误)。
注意:
Value of c
is promoted to int
in expression ~c
, but type of c
is still unsigned char
- its type does not. Don't be confused.
Important: result of ~
operation is of int
type!, check below code (I don't have vs-compiler, I am using 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;
}
compile it, and run:
$ gcc -std=gnu99 -Wall -pedantic x.c -o x
$ ./x
sizeof(int) = 4,
sizeof(unsigned char) = 1
sizeof(~c) = 4
Notice: size of result of ~c
is same as of int
, but not equals to unsigned char
— result of ~
operator in this expression is int
! that as mentioned 6.5.3.3 Unary arithmetic operators
- The result of the unary
-
operator is the negative of its (promoted) operand. The integer
promotions are performed on the operand, and the result has the promoted type.
现在,正如@haccks在他的answer中解释的那样,在32位机器上,对于c = 4
的值,~c
的结果是:
1111 1111 1111 1111 1111 1111 1111 1011
在十进制中,它是-5
——这是你的第二个代码的输出!
在你的第一个代码中,还有一行很有趣需要理解b = ~c;
,因为b
是一个unsigned 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
1111 1011
的十进制等价物是
251
。你也可以使用以下方式获得相同的效果:
printf("\n ~c = %d", ~c & 0xFF);
或者按照@ouah在他的answer中建议,使用显式转换。