思考:
int ch = ‘A’;
int ch1 = 65;
int ch2 = 321;
printf("%c %c %c\n", ch, ch1, ch2);的输出结果是什么?
char ch = 0;
char ch1 = -128;
char ch2 = 128;
char ch3 = 256;
printf("%d %d %d %d\n", ch, ch1, ch2, ch3);的输出结果又是什么?
我们都知道,char字符类型根据ASCII码表中的ascii值显示字符,并且char类型的范围为-128~127
,但是像char ch = 256;
这种赋值仍然可以赋值成功,通过上面的思考我已经有了一定的猜想。
输出结果:A A A
0 -128 -128 0
猜想:
char类型的范围为-128~127
char ch1 = 128;
输出:-128
分析:127
为char的右极限值,-128
位char的左极限值char ch3 = 256;
输出:0
分析:256
为char的容量
总结:当输入的值大于char的一边极限值时,会从另一边开始以剩余的差值计算。
测试:
#include <stdio.h>
int main()
{
char ch;
for (int i = 0; i <= 512; i++)
{
ch = i;
printf("%d -> %d\n", i, ch);
}
return 0;
}
输出:
0 -> 0
1 -> 1
2 -> 2
省略部分输出结果 ……
127 -> 127 //char -128~127
128 -> -128
129 -> -127
省略部分输出结果 ……
254 -> -2
255 -> -1
256 -> 0
257 -> 1
258 -> 2
省略部分输出结果 ……
510 -> -2
511 -> -1
512 -> 0
很好,测试结果与我们猜想的一致,那么问题又来了,究竟是不是所有的类型赋值方法都是这样呢,还是只有char是个特例呢?
unsigned char测试
//unsigned char 0 ~ 255
unsigned char ch = 0; //0
unsigned char ch1 = -1; //255 = 256 -1
unsigned char ch2 = 256; //0 = 0+256
unsigned char ch3 = 250; //250 = 0+255
unsigned char ch4 = 300; //44 = 256+44
unsigned char ch5 = 400; //144 = 256+144
unsigned char ch6 = -200; //56 = 256 - 200
unsigned char类型容量为256,则256的倍数全为0(循环一圈回到0位置),可以根据这一特点计算。
例如:
- 正数n(>255)
- 256的倍数
0
- n == k×256+x
x
- 256的倍数
- 负数n(>255)
- 256的倍数
0
- n == -k×256+x
x
- 256的倍数
对于char类型,也可以根据取值范围整理出相应的计算公式。大家可对照测试结果自行整理。
此外,int
unsigned in
short
……等中应该也成立,这里就不再做测试。
另附上
int 越界处理:可以通过位运算的方式求得各类型的取值范围。
'\ddd’转义字符与八进制转换:char类型的其他赋值方式
2021.5.14更新…
使用 bitset 库输出变量中保存的的二进制数值。
#include <iostream>
#include <bitset>
using namespace std;
int main()
{
char ch = 0;
for (int i = 0; i < 300; i++)
{
ch = i;
bitset<8> bit(*(unsigned char*)(&ch)); // 取ch所在地址中保存的补码形式的二进制
cout << i << "\t" << (int)ch << "\t" << bit << endl;
}
return 0;
}
在输出结果中,我们可以看到
由上图的结果可以看到,ch在127之后,数值由127变成了-128,继续执行下去当ch为-1时又回到最初的 0 值。就像上文画的闭环一般, 0->127,-128->0->127…
下面我们从二进制的角度分析:
在计算机中,有符号数字都使用补码表示。对于char类型的一字节8位数字,第一位是符号位,其余位是数值位。根据上图可得:
- 127 的二进制为 0111 1111(补码)
- -128的二进制为 1000 0000(补码)
由于 127 + 1 后进位,使得高位溢出到符号位。而符号位标志着数组的正负,则数值从原先的正数变为负数。 而 1000 0000(补码) 是-128的补码,则他表示有符号数 -128 。
需要注意的是:-128只有补码表示。
对于一字节8位的数而言,实际表示数值大小的只有7位,最高值只能取到127 。而-128却是一个特殊的存在。
我们都知道(负数的)原码与补码之间的关系是,原码 -> 取反+1
=> 补码 。对于正数而言,不区分原码/反码/补码,都用自身原码表示,只有负数才进行区分。
对于有符号的数,我们在首位取一位作为符号位,表示数值的正负。而对于0,我们就有两种表示方法:即 +0 ⇒ 0000 0000
、 -0 ⇒ 1000 0000
,这显然是不合理的。
+0与-0应该表示的同一个数,而且我们可以发现,
1000 0000(原)
作为原码时,它的补码任然是1000 0000(补)
(高位溢出,丢弃)- -127
1111 1111(原)
减一后(加负一)的二进制值也是1000 0000
。而 -127 - 1 的值为 -128 。
则,我们可以用 补码 1000 0000 表示 -128 。而且由于我们取消“负0”这个概念,就空出了一个二进制位组合可以表示其他数值(用于表示-128)。
由之前的分析可知,补码 1000 0000 的原码值可以表示 负0, 然而我们并不需要“负0”,因此对于 补码 1000 0000 我们并不研究其原码,它只有补码有意义,表示 -128 。因此,在127加一之后就变成了-128 。
数值 | 原码 | 补码(取反,加一) |
---|---|---|
127 | 0 111 1111 | 0 000 0001 |
-128 | 1 000 0000 | 1 000 0000 |
… | ||
-1 | 1 000 0001 | 1 111 1111 |
0 | 0 000 0000 | 0 000 0000 |