- char a = -1; // 机器码为 0xff
- unsigned char b = 254; // 机器码 0xfe
- if (a <= b){
- printf("a <= b\n");
- }
- else{
- printf("a> b\n");
- }
上述代码输出结果: a> b
赋值用机器码写入内存
虽然我们以十进制为两个变量赋值, 但是变量值在内存中是以二进制机器码的形式存在. 如果十进制数是负数, 它就以补码的形式存放在内存中. 比如 "a = -1",a 的真值以二进制表示为 "1000 0001", 高位是符号位, 其余位表示绝对值; 它的反码是 "1111 1110", 补码是 "1111 1111", 所以内存中某个存放变量 a 的字节的数是 0xff. 而正数的补码就是原码, 不需要转换, 所以内存中某个存放变量 b 的字节的数是 0xfe.(有关机器码和补码知识请戳 https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html)
运行时不同类型变量的比较存在类型转换
当正在比较的两个变量类型不同时, 会发生类型转换. 有符号 char 型和无符号 char 型比较时, 有符号临时转换成无符号 (机器码不变, 只是编译器处理这个变量的方法改变).a 临时转成无符号后机器码仍然时 0xff, 但是编译器把它作为无符号处理 -- 即没有符号位, 取值范围时 [0, 255], 所以临时变量值是 255, 自然比 b 大.
那么字符型和整型变量发生类型转换时需要注意哪些呢?
一字节 "字符型" -> (转换为) 四字节 "整型", 字节数较少的字符型变量会向高位扩展, 具体补'0'还是补'1', 根据字符型变量自身类型和高位符号两者决定. 下面看四个例子.
例一:
- char a = 0xff;
- unsigned b = 0xffffffff;
- if (a == b){
- printf("equal.\n");
- }
- else{
- printf("not equal\n");
- }
上述代码输出结果: equal. 即补'1'.
例二:
- char a = 0xff;
- int b = 0xffffffff;
- if (a == b){
- printf("equal.\n");
- }
- else{
- printf("not equal\n");
- }
上述代码输出结果: equal. 即补'1'.
例二和例一只有变量 b 的类型不同, 由此看出向高地址补位的动作不受要转向的那个类型所影响.
例三:
- unsigned char a = 0xff;
- unsigned b = 0xffffffff;
- if (a == b){
- printf("equal.\n");
- }
- else{
- printf("not equal.\n");
- }
上述代码输出结果: not equal.. 即补'0'.
例三和例一只有变量 a 的类型不同, 由此看出向高地址补位的动作受变量本身类型所影响.
例四:
- char a = 0x7f;
- unsigned b = 0xffffffff;
- if (a == b){
- printf("equal.\n");
- }
- else{
- printf("not equal.\n");
- }
上述代码输出结果: not equal.. 即补'0'.
例四和例一只有变量 a 的值不一样, 例四中变量 a 的高位是 0, 因此向高位补'0', 由此有符号型向高地址补位的动作受变量符号位的值所影响.
而四字节 "整型"-> (转换为) 一字节 "字符型" , 就是单纯地把低位一字节的内容赋值给字符型变量.
char 型数据溢出情况
- char a = 64;
- a *= 2;
- if (a>= 0){
- printf("a>= 0");
- }
- else{
- prinf("a <0");
- }
上述代码输出结果: a < 0.
虽然以十进制数'128'赋值给变量, 但实际存入内存中的机器码是 0x80, 编译时以有符号字符型处理这个字节. 这个值符号位是'1', 表示负数, 对其余位求补码 -- 结果换算成十进制, 并加负号, 就是这个机器码的真值, 即'-128'. 所以小于'0'. 例子中虽然 0x80 在一个字节所能表示的数值范围内, 但是超过 char 型所能表示的正数范围, 这是 char 型数据溢出的一个例子.
unsigned char 型数据溢出情况
- unsigned char a = 128;
- do {
- a *= 2;
- printf("%x", a);
- } while (a <= 256)
上述代码会不停循环.
当变量 a 从 0x80 乘 2 后, 机器码是 0x100. 由于'a'只能存储一个字节的数据, 所以取结果的低位一字节, 即 0x00, 这样从 0 -> 255 -> 0 循环下去. 这是 unsigned char 型数据溢出的一个例子.
另外举一个误把 unsigned char 型当作负数处理地例子, 虽然不可能发生, 但有必要了解一下其中原因:
- unsigned char a = 0x0a;
- do {
- --a;
- printf("%x", a);
- } while (a>= 0)
上述代码会不停循环.
当变量 a 从 0x0 自减后, 机器码是 0xff. 因为计算机运算中把减法当作两数的补码相加来做,(0 - 1) 表达式在计算机运算中解释为 (0x0 + 0xff), 所以结果是 0xff.
最后举一个 char 型最小负数取相反数溢出的例子:
- char a = -128;
- char b = -a;
- if (b> 0){
- printf("b> 0\n");
- }
- else{
- printf("b <= 0\n");
- }
上述代码输出结果: b <= 0.
|a | 的真值用二进制表示 "1000 0000", 用补码表示同样是 "1000 0000", 最后由于是负数, 高位置为'1', 结果是 "1000 0000", 这个 0x80 的 char 型机器码的特殊之处在于符号位同时表示数值.'b'被编译器处理为 - 128, 所以输出 "b <= 0".
来源: http://www.bubuko.com/infodetail-2877887.html