char a = -; //机器码为0xff
unsigned char b = ; //机器码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 = ;
a *= ;
if (a >= ){
printf("a >= 0");
}
else{
prinf("a < 0");
}

上述代码输出结果:a < 0。

  虽然以十进制数‘128’赋值给变量,但实际存入内存中的机器码是0x80,编译时以有符号字符型处理这个字节。这个值符号位是‘1’,表示负数,对其余位求补码——结果换算成十进制,并加负号,就是这个机器码的真值,即‘-128’。所以小于‘0’。例子中虽然0x80在一个字节所能表示的数值范围内,但是超过char型所能表示的正数范围,这是char型数据溢出的一个例子。

unsigned char型数据溢出情况

unsigned char a = ;
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 >= )

上述代码会不停循环。

  当变量a从0x0自减后,机器码是0xff。因为计算机运算中把减法当作两数的补码相加来做,(0 - 1)表达式在计算机运算中解释为(0x0 + 0xff),所以结果是0xff。

最后举一个char型最小负数取相反数溢出的例子:

char a = -;
char b = -a;
if (b > ){
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"。

最新文章

  1. seq 显示00 01的格式
  2. SQL PROMPT 取消dbo前缀
  3. Android -- 资源使用和总结经验分享
  4. 如何对应用服务性能问题诊断(Tomcat、Weblogic中间件)
  5. 2014年CCNU-ACM暑期集训总结
  6. restful Api 风格契约设计器:Swagger-editor和swagger-ui的应用
  7. HTTP协议扫盲(八 )响应报文之 Transfer-Encoding=chunked方式
  8. Android组件化探索与实践
  9. .Net Core应用框架Util介绍(一)
  10. html-伪类
  11. (zhuan) Some Talks about Dual Learning
  12. Yarn使用简介
  13. MySQL update 语句与标准SQL不同的地方
  14. Android 去掉标题全屏显示
  15. 4类Storage方案(AS开发实战第四章学习笔记)
  16. MVC捕获数据保存时的具体字段验证错误代码
  17. 项目中jquery插件ztree使用记录
  18. Spring Cloud实战之初级入门(四)— 利用Hystrix实现服务熔断与服务监控
  19. Android Service 生命周期
  20. [Android Pro] android中permission_group与permisson区别、作用

热门文章

  1. hexo 建站参考
  2. 4.26 IO流
  3. ESP8266最小系统
  4. python中使用redis发布订阅者模型
  5. vue动态设置初始页
  6. 大神教你如何解决Linux系统80端口被占用
  7. NodeJs之word文件生成与解析
  8. 查看电脑系统参数(Windows)
  9. plus.webview更新上一个页面的信息
  10. jmap -histo pid 输出的[C [B [I [S methodKlass constantPoolKlass含义