【发布时间】:2018-01-19 12:43:30
【问题描述】:
unsigned char a = 10,b;
b = ~a;
printf(" %d \n ",b);
输出:
245
如果我使用 int 而不是 unsigned char ,则相同的程序将 o/p 更改为
-11
【问题讨论】:
标签: c unsigned-char negate
unsigned char a = 10,b;
b = ~a;
printf(" %d \n ",b);
输出:
245
如果我使用 int 而不是 unsigned char ,则相同的程序将 o/p 更改为
-11
【问题讨论】:
标签: c unsigned-char negate
在 C 中,int 和 char 必须是类型
如果用户给出 int [16 位] 或 char [8 位],则编译器默认采用带符号的。对于未签名的,用户需要明确提及。
unsigned char 范围从 0 到 255 [8 位]。
signed char 范围从 -128 到 0 到 127 [8 位]。
根据您的程序,
案例一:
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.
案例2:
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 数据类型。
【讨论】:
变量a 包含unsigned char 值10。假设正统设置具有8 位char 和32 位int 类型,那么a 中的位是:
0000 1010
b 中存储的值是a 中的按位倒数,这意味着它包含以下位,对应于十进制的 245:
1111 0101
当unsigned char 传递给printf():
printf("a = %d\n", a);
printf("b = %d\n", b);
比int 短的类型(字符类型和short 类型——我假设short 是16 位类型,通常但不一定是这种情况)会自动提升为@987654337 @。由于源类型为 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's complement arithmetic(存储)的工作方式,b 中的位模式对应于-11。而当a 和b 的这些版本传递给printf() 时,不需要进行转换,所以printf() 将10 和10 和-11 视为值,并相应地打印。
重点是推广操作。
在 C 标准中,它说:
6.5.2.2 函数调用
¶7 如果表示被调用函数的表达式具有包含原型的类型, 参数被隐式转换,就像通过赋值一样,转换为 对应的参数,取每个参数的类型为不合格版本 其声明的类型。函数原型声明器中的省略号表示 参数类型转换在最后一个声明的参数之后停止。默认参数 提升是在尾随参数上执行的。
当然printf()的声明类型是:
int printf(const char * restrict format, ...);
因此,a 和 b 的值位于最后声明的参数之后,并受“默认参数提升”的约束。它们在第 6 段中(部分)指定:
¶6 如果表示被调用函数的表达式的类型不包含 原型,整数提升对每个参数执行,参数 将类型
float提升为double。这些被称为 default 参数 促销。 …
你必须再往前走一点才能找到整数促销:
6.3.1 算术操作数
6.3.1.1 布尔值、字符和整数
¶2 可以在表达式中使用 int 或 unsigned int 的任何位置 使用:
- 具有整数类型的对象或表达式(
int或unsigned int除外) 其整数转换等级小于或等于int的等级并且unsigned int。_Bool、int、signed int或unsigned int类型的位字段。如果
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
【讨论】:
~ 本身引起的整数提升,因为这会导致符号的无声变化。这发生在 printf 默认参数提升之前。
8b 到32b 的char 到int 的提升。我的错。
无符号字符
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() 时符号被保留。
内部
Two's complement 是 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位的unsigned,这意味着值0..255。 (2^8 -1)
所以对于signed char,你有-127..127 的值。 (+/-)2^7 -1
无符号字符仅表示:在执行算术运算时,使用最高有效位而不是将其视为 +/- 符号的位标志。
【讨论】:
~ 实际上根本不适用于 unsigned char 类型,而是适用于 int 类型,因为 整数提升,这是这个问题的核心。这意味着代码if(~a < 0) puts("oops") 将打印“oops”。但是,如果操作写成 int b = (unsigned char)~a; 之类的东西,结果将保持 245。
每当~ 用于小整数类型 的操作数时,例如char、unsigned char、short 等,该操作数总是隐式提升为int 类型.这被称为整数提升规则。
这意味着无论您的示例中的a 是什么字符类型,它都会被提升为int,这是一个有符号类型。表达式~a 等价于~(int)10。结果将是(假设是 32 位 CPU)0xFFFFFFF5,用二进制补码表示为 -11。
但是,当您将此数字存储回无符号字符时,它将被转换为值为245 的无符号类型。当您将b 声明为int 时,不会发生这种转换并且值仍然是-11。
以上就是为什么~ 是一个相当危险的运算符使用的原因。在更复杂的表达式中,它可能会产生由符号的无声变化引起的非常微妙的错误。
【讨论】:
int 变量存储32 bits,第一位代表数字的符号。
如果该位打开,则该数字为负数。
负数表示如下:
-1 | 11111...111
-2 | 11111...110
-3 | 11111...101
-4 | 11111...100
序列几乎与正整数相同,但以reversed 的顺序排列。
【讨论】:
int 可以窄至 16 位,也可以宽于 32 位。