unsigned char a = 10,b;
b = ~a;
printf(" %d \n ",b);
输出:
245
如果我使用相同的程序int
而不是unsigned char
o/p 更改为
-11
unsigned char a = 10,b;
b = ~a;
printf(" %d \n ",b);
输出:
245
如果我使用相同的程序int
而不是unsigned char
o/p 更改为
-11
该变量a
包含unsigned char
值 10。假设具有 8 位char
和 32 位int
类型的正统设置,则其中的位a
为:
0000 1010
存储的值b
是 in 的按位倒数a
,这意味着它包含以下位,对应于十进制的 245:
1111 0101
当 anunsigned char
传递给printf()
:
printf("a = %d\n", a);
printf("b = %d\n", b);
短于int
(字符类型和short
类型——我假设它short
是 16 位类型,通常但不一定是这种情况)的类型会自动提升为int
. 由于源类型是unsigned char
,因此该值被转换为正值int
——示例代码中的 10 和 245。
将类型从 更改unsigned char
为int
,事情就会改变。
a: 0000 0000 0000 0000 0000 0000 0000 1010
b: 1111 1111 1111 1111 1111 1111 1111 0101
并且由于2 的补码算术(存储)的工作方式,位模式b
对应。并且当这些版本的and被传递给时,不需要转换,因此将and视为值,并相应地打印。-11
a
b
printf()
printf()
10
-11
重点是推广操作。
在 C 标准中,它说:
6.5.2.2函数调用
¶7 如果表示被调用函数的表达式具有包含原型的类型,则参数将隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型视为它声明的类型。函数原型声明器中的省略号会导致参数类型转换在最后一个声明的参数之后停止。默认参数提升是在尾随参数上执行的。
当然,声明的类型printf()
是:
int printf(const char * restrict format, ...);
因此,值a
和b
位于最后声明的参数之后,并受“默认参数提升”的影响。它们在第 6 段中(部分)指定:
¶6 如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有类型的参数
float
提升为double
. 这些被称为默认参数提升。…</p>
您必须再往前走一点才能找到整数促销:
6.3.1 算术操作数
6.3.1.1 布尔值、字符和整数
¶2 可以在任何使用 int 或 unsigned int 的表达式中使用以下内容:
- 具有整数类型(除
int
or之外unsigned int
)的对象或表达式,其整数转换等级小于或等于int
and 的等级unsigned int
。_Bool
,int
,signed int
或类型的位域unsigned int
。如果 an
int
可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为int
; 否则,将其转换为unsigned int
. 这些被称为整数促销。58)整数提升不会改变所有其他类型。¶3整数促销保留价值,包括符号。如前所述,“plain”
char
是否被视为已签名是实现定义的。
等级的概念在 §6.3.1.1 ¶1 中定义,我不会引用它(它相当长且非常详细)。
关键是较小的类型(如char
)被提升为int
,并保留包括符号在内的值。你所看到的完全符合这一点。
#include <stdio.h>
int main(void)
{
unsigned char a1 = 10;
unsigned char b1 = ~a1;
printf("a1 = %d, b1 = %d\n", a1, b1);
int a2 = 10;
int b2 = ~a2;
printf("a2 = %d, b2 = %d\n", a2, b2);
return 0;
}
输出:
a1 = 10, b1 = 245
a2 = 10, b2 = -11
无论何时~
用于小整数类型的操作数,例如char
,unsigned char
等short
,该操作数总是隐式提升为 type int
。这称为整数提升规则。
这意味着无论a
您的示例中的 char 类型是什么,它都会被提升为int
有符号类型。表达式~a
等价于~(int)10
。结果将是(假设是 32 位 CPU)0xFFFFFFF5
,用二进制补码表示为-11
.
但是,当您将此数字存储回无符号字符时,它将转换为具有 value 的无符号类型245
。当您声明b
asint
时,不会发生这种转换并且值保持不变-11
。
~
以上就是使用相当危险的操作符的原因。在更复杂的表达式中,它可以创建由符号的无声变化引起的非常微妙的错误。
无符号的字符
a
|128|64|32|16|8|4|2|1|
|0 |0 |0 |0 |1|0|1|0|
等于10
~
是位否定
(unsigned char)~a
|128|64|32|16|8|4|2|1|
|1 |1 |1 |1 |0|1|0|1|
等于245
由于8b
char
小于32b
int
,值被扩展为,32b
但在调用时保留符号printf()
。
诠释
二进制补码用于usually
有符号运算。让我们期待sizeof(int)
等于4B
(32bit
's)。(大小int
取决于架构。)
10
在 4B
|0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |1|0|1|0|
~10
在 4B
|1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |1|1|1|1| |0|1|0|1|
获得价值decimal
~
否定值所以你有了
- |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |0|0|0|0| |1|0|1|1|
它等于-11
十进制
unsigned char 在按位求反运算中有什么用?
通过使用unsigned char
你得到了8
bit's unsigned
which mean's values 0..255
。(2^8 -1)
所以因为signed char
你有价值观-127..127
。 (+/-)2^7 -1
unsigned char 只是意味着:在执行算术运算时,使用最高有效位而不是将其视为 +/- 符号的位标志。
在 C 中,int 和 char 必须类型
如果用户给出 int [16 位] 或 char [8 位],则编译器默认采用带符号的。对于未签名的,用户需要明确提及。
unsigned char 范围从 0 到 255 [8 位]。
有符号字符的范围从 -128 到 0 到 127 [8 位]。
根据你的程序,
情况1:
a 是无符号字符,输出 245 在 0 到 255 的范围内。所以你得到的输出是 245。
0, 1, 2, 3, 4, ..................... 254, 255
| |
|<- <- <- <- <- <- <- <- <- <- <- <- <- <- |
example:
unsigned char a = 257; // more than 255.
printf("a = %d", a);
**Output** : a = 1. // [ 0,1,...255,0 [256th], **1** [257th]
unsigned char a = 129; // with in range of 255.
printf("a = %d", a);
**Output** : a = 129.
案例二:
a 是有符号字符,相同的输出 245 从 0 到 127 开始,然后从 -128、-127、-126、..... -11(第 245 位)开始并打印 -11。
-128, -127, -126, ..... -11, -10, .. -1, 0, 1, 2, 3, .....126, 127
| |
|<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-<-|
example:
char a = 257; // more than 255.
printf("a = %d", a);
**Output** : a = 1. // [ 0,1,...127,-128,-127,....-1, 0 [256th], **1** [257th].
char a = 129; // with in range of 255.
printf("a = %d", a);
**Output** : a = -127. // [ 0,1,...127,-128 [128th], **-127** [129th].
同样的情况也适用于 int 数据类型。
int
变量存储32 bits
,第一位代表数字的符号。如果该位打开,则该数字为负数。
负数表示如下:
-1 | 11111...111
-2 | 11111...110
-3 | 11111...101
-4 | 11111...100
该序列几乎与正整数相同,但按reversed
顺序排列。