[英]Why does the complement behave differently through printf?
I was reading a chapter on bitwise operators, I came across 1's complement operator program and decided to run it on Visual C++. 我正在阅读关于按位运算符的章节,我遇到了1的补码运算符程序,并决定在Visual C ++上运行它。
int main ()
{
unsigned char c = 4, d;
d = ~c;
printf("%d\n", d);
}
It gives the valid output: 251
它给出了有效的输出:
251
Then instead of using d
as a variable to hold the value of ~c
, I decided to directly print the value of ~c
. 然后,而不是使用
d
为变量来保存的值~c
,我决定直接打印的值~c
。
int main ()
{
unsigned char c=4;
printf("%d\n", ~c);
}
It gives the output -5
. 它给出了输出
-5
。
Why didn't it work? 它为什么不起作用?
In this statement: 在这个声明中:
printf("%d",~c);
the c
is converted to int
1 type before ~
(bitwise complement) operator is applied. 在应用
~
(按位补码)运算符之前 , c
被转换为int
1类型。 This is because of integer promotions , that are invoked to operand of the ~
. 这是因为整数提升 ,被调用到
~
操作数。 In this case an object of unsigned char
type is promoted to (signed) int
, which is then (after ~
operator evaluation) used by printf
function, with matching %d
format specifier. 在这种情况下,
unsigned char
类型的对象被提升为(signed) int
,然后(在~
运算符求值之后)由printf
函数使用,匹配%d
格式说明符。
Notice that default argument promotions (as printf
is a variadic function) does not play any role here, as object is already of type int
. 请注意, 默认参数提升 (因为
printf
是一个可变参数函数)在这里不起任何作用,因为对象已经是int
类型。
On the other hand, in this code: 另一方面,在这段代码中:
unsigned char c = 4, d;
d = ~c;
printf("%d", d);
the following steps occur: 发生以下步骤:
c
is a subject to integer promotions because of ~
(in the same way, as described above) ~
(以同样的方式,如上所述) c
是整数促销的主题 ~c
rvalue is evaluated as (signed) int
value (eg -5
) ~c
rvalue被评估为(有符号) int
值(例如-5
) d=~c
makes an implicit conversion from int
to unsigned char
, as d
has such type. d=~c
进行从int
到unsigned char
的隐式转换,因为d
具有这种类型。 You may think of it as the same as d = (unsigned char) ~c
. d = (unsigned char) ~c
。 Notice that d
cannot be negative (this is general rule for all unsigned types). d
不能为负数(这是所有无符号类型的一般规则)。 printf("%d", d);
invokes default argument promotions , thus d
is converted to int
and the (nonnegative) value is preserved (ie the int
type can represent all values of unsigned char
type). d
转换为int
并保留(非负)值(即int
类型可以表示unsigned char
类型的所有值)。 1) assuming that int
can represent all values of the unsigned char
(see TC's comment below), but it is very likely to happen in this way. 1)假设
int
可以表示unsigned char
所有值(参见下面的TC 注释 ),但很可能以这种方式发生。 More specifically, we assume that INT_MAX >= UCHAR_MAX
holds. 更具体地说,我们假设
INT_MAX >= UCHAR_MAX
成立。 Typically the sizeof(int) > sizeof(unsigned char)
holds and byte consist of eight bits. 通常,
sizeof(int) > sizeof(unsigned char)
成立,byte由8位组成。 Otherwise the c
would be converted to unsigned int
(as by C11 subclause §6.3.1.1/p2), and the format specifier should be also changed accordingly to %u
in order to avoid getting an UB (C11 §7.21.6.1/p9). 否则,
c
将被转换为unsigned int
(如C11子条款§6.3.1.1/ p2所示),并且格式说明符也应相应地更改为%u
,以避免获得UB(C11§7.21.6.1/ p9) 。
char
is promoted to int
in printf
statement before the operation ~
in second snippet. 在第二个代码段中的操作
~
之前, char
被提升为printf
语句中的int
。 So c
, which is 所以
c
,这是
0000 0100 (2's complement)
in binary is promoted to (assuming 32-bit machine) 在二进制中被提升为(假设32位机器)
0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x
and its bit-wise complement is equal to the two's complement of the value minus one ( ~x = −x − 1
) 并且它的逐位补码等于值的二进制补码减一(
~x = −x − 1
)
1111 1111 1111 1111 1111 1111 1111 1011
which is -5
in decimal in 2's complement form. 以2的补码形式,以十进制表示
-5
。
Note that the default promotion of char
c
to int
is also performed in 请注意,也将执行
char
c
到int
的默认提升
d = ~c;
before complement operation but the result is converted back to unsigned char
as d
is of type unsigned char
. 在补充操作之前,但结果将转换回
unsigned char
因为d
的类型为unsigned char
。
In simple assignment (
=
), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.在简单赋值(
=
)中,右操作数的值将转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。
and 和
The type of an assignment expression is the type the left operand would have after lvalue conversion.
赋值表达式的类型是左值操作数在左值转换后将具有的类型。
To understand behavior of your code, you need to learn the concept called 'Integer Promotions' (that happens in your code implicitly before bit wise NOT operation on an unsigned char
operand) As mentioned in N1570 committee draft: 要理解代码的行为,你需要学习一个名为“整数促销”的概念(在你的代码中隐式地在
unsigned char
操作数上进行逐位运算之前发生)如N1570委员会草案所述:
§ 6.5.3.3 Unary arithmetic operators
§6.5.3.3一元算术运算符
- The result of the
~
operator is the bitwise complement of its (promoted) operand (that is, each bit in the result is set if and only if the corresponding bit in the converted operand is not set).~
运算符的结果是其(提升的)操作数的按位补码(即,当且仅当未设置转换后的操作数中的相应位时,才会设置结果中的每个位)。 The integer promotions are performed on the operand, and the result has the promoted type .整数提升在操作数上执行,结果具有提升类型 。 If the promoted type is an " 'unsigned type', the expression
~E
is equivalent to the maximum value representable in that type minusE
".如果提升类型是“'无符号类型',则表达式
~E
等于该类型中可表示的最大值减去E
”。
Because unsigned char
type is narrower than (as it requires fewer bytes) int
type, - implicit type promotion performed by abstract machine(compiler) and value of variable c
is promoted to int
at the time of compilation (before application of the complement operation ~
). 因为
unsigned char
类型比(因为它需要更少的字节) int
类型更窄, - 抽象机器(编译器)执行的隐式类型提升和变量c
值在编译时被提升为int
(在应用补码操作之前~
)。 It is required for the correct execution of the program because ~
need an integer operand. 它是正确执行程序所必需的,因为
~
需要一个整数操作数。
§ 6.5 Expressions
§6.5表达式
- Some operators (the unary operator
~
, and the binary operators<<
,>>
,&
,^
, and|
, collectively described as bitwise operators) are required to have operands that have integer type .一些运算符(一元运算符
~
,以及二元运算符<<
,>>
,&
,^
和|
,统称为按位运算符) 需要具有整数类型的操作数 。 These operators yield values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.这些运算符产生的值取决于整数的内部表示,并且具有已签名类型的实现定义和未定义方面。
Compilers are smart-enough to analyze expressions, checks semantics of expressions, perform type checking and arithmetic conversions if required. 编译器足够智能 - 足以分析表达式,检查表达式的语义,执行类型检查和算术转换(如果需要)。 That's the reason that to apply
~
on char
type we don't need to explicitly write ~(int)c
— called explicit type casting (and do avoid errors). 这就是在
char
类型上应用~
的原因,我们不需要显式写入~(int)c
- 称为显式类型转换(并避免错误)。
Note: 注意:
Value of c
is promoted to int
in expression ~c
, but type of c
is still unsigned char
- its type does not. c
值在表达式~c
被提升为int
,但c
类型仍然是unsigned char
- 它的类型不是。 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): 重要:
~
操作的结果是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; }
compile it, and run: 编译它,然后运行:
$ gcc -std=gnu99 -Wall -pedantic xc -ox $ ./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
! 注意 :
~c
的结果大小与int
相同,但不等于unsigned char
- 此表达式中~
运算符的结果为int
! that as mentioned 6.5.3.3 Unary arithmetic operators 如上所述6.5.3.3一元算术运算符
- 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.整数提升在操作数上执行, 结果具有提升类型。
Now, as @haccks also explained in his answer -that result of ~c
on 32-bit machine and for value of c = 4
is: 现在,正如@haccks在他的回答中解释的那样 - 在32位机器上的
~c
和c = 4
值的结果是:
1111 1111 1111 1111 1111 1111 1111 1011
in decimal it is -5
— that is the output of your second code ! 在十进制中它是
-5
- 这是你的第二个代码的输出!
In your first code , one more line is interesting to understand b = ~c;
在你的第一个代码中 ,另外一行很有意思理解
b = ~c;
, because b
is an unsigned char
variable and result of ~c
is of int
type, so to accommodate value of result of ~c
to b
result value (~c) is truncated to fit into the unsigned char type as follows: ,因为
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
Decimal equivalent of 1111 1011
is 251
. 十进制等值
1111 1011
是251
。 You could get same effect using: 使用以下方法可以获得相同的效
printf("\n ~c = %d", ~c & 0xFF);
or as suggested by @ouah in his answer using explicitly casting. 或者@ouah在他的回答中使用显式强制转换的建议。
When applying the ~
operator to c
it gets promoted to int
, the result is an int
as well. 将
~
运算符应用于c
它会被提升为int
,结果也是一个int
。
Then 然后
unsigned char
and then promoted to signed int
and printed. unsigned char
,然后提升为signed int
并打印。 signed int
. signed int
。 It gives the op -5.
它给出了op -5。 why it didn't work?
为什么它不起作用?
Instead of: 代替:
printf("%d",~c);
use: 使用:
printf("%d", (unsigned char) ~c);
to get the same result as in your first example. 获得与第一个示例中相同的结果。
~
operand undergoes integer promotion and default argument promotion are applied to argument of variadic functions. ~
操作数经过整数提升,默认参数提升应用于可变函数的参数。
Integer promotion, from the standard: 整数推广,来自标准:
If the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.
如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数应转换为带有符号整数类型的操作数的类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.