乱读天书, 不求甚解
周祎骏的个人云笔记
Toggle navigation
乱读天书, 不求甚解
主页
Linux:系统配置
Linux:用户管理
Linux:优化排错
Linux:进程调度
Linux:文件系统
Linux:网络
Linux:系统服务
Linux:安全
Linux:内核
容器:Docker
容器:containerd
容器编排:Kubernetes
IAC:Terraform
大数据:Hadoop
大数据:Zookeeper
大数据:Hbase
消息队列:rsyslog
消息队列:kafka
数据库:MySQL
数据库:MongoDB
搜索引擎:Elasticsearch
时序数据库:OpenTSDB
网站服务:Nginx
编程:Bash
编程:Perl
编程:Python
编程:C
编程:JAVA
编程:Rust
版本控制:gitlab
知识管理:docusaurus
常用小工具
关于我
标签
C 0.11 计算溢出
2018-06-17 08:23:04
134
0
0
admin
> 源码/反码/补码/有符号/无符号/溢出 #有符号数/无符号数 如果二进制的第一位用于表达正负,0=正,1=负,那么这个变量就是有符号数。 如果二进制的所有位都用于表达数值,那么这就是一个无符号数。 * 无符号数主要出现在C语言中,比较底层的东西上,因为快。JAVA就没有无符号数这个东西。 * C语言中,有符号数转无符号数或者无符号数转有符号数的时候,实际数据并没有改变,只是系统用有符号数的规则或者无符号数的规则去解析数据的区别。 * 当一个有符号数和一个无符号数做运算时,系统默认把有符号数转成无符号数。 #源码 源码就是数字转化成二进制以后的样子,3就是0011 #反码 因为负数的存在,所以我们把一个负数的二进制定义为其正数二进制的取反,-3就是1100。 相关解释: * 第一位正数是0,取反就是1,变成负数 * 其它位取反,保证其与其正数相加能得到0这个结果。 0011 + 1100 = 11111 舍去最高位就是1111 就是-0 #补码 解决同时存在正0和负0的问题。所有负数二进制末位+1,-3就是1101 这样-3 +3 就是1101 + 0011 = 10000,舍去最高位就是0000,就是0。 这也就是为什么一般性变量能表达的负数绝对值要比正数绝对值大1。 * 系统中的值一般都是用补码来表示。 *** #溢出 溢出是计算的结果超出了变量类型能表达的范围,CPU不认为溢出是错误,甚至很多设计会利用溢出,比如前面提到的正数与负数的和,实际上就是溢出后舍去最高位的结果。 > 例子: 40分钟+40分钟=20分钟,实际上应该是1小时20分钟,但是我们只看分针没看时针,所以取得了20分钟这个结果,这明显是错误的结果。 网上对溢出错误有分歧,很多人坚持无符号数不可能出现溢出错误,这是站在CPU和编译器的角度来考虑问题,因为在无符号数的情况下,CPU的每一个位都做了正确的计算,可能最终位不够,但是设计就是这样的。有符号数有溢出错误的原因是有符号的数的最高位用来表示正负,但是当数据太大的时候最高位可能会被计算覆盖,这就违反了设计初衷。 我不想玩文字游戏,我这里讨论溢出导致的Bug,即程序运行结果与预期不符。 **如何判断溢出** 两个正数相加,结果为负,则溢出 两个负数相加,结果为正,则溢出 还有一个流氓办法: 用更多的位的类型计算,然后再转回来,看看结果是不是不变。 ```c #include <stdio.h> #include <stdlib.h> #include <inttypes.h> main(int argc,char *argv[]) { int n = atoi(argv[1]); int64_t n_64 = (int64_t) n + 1; printf("64=%" PRId64 ",32=%d,overflow=%d\n",n_64,(int)n_64,n_64==(int)n_64); } ``` 运行结果 ``` [root@test ~]# ./a.out 2147483646 64=2147483647,32=2147483647,overflow=1 [root@test ~]# ./a.out 2147483647 64=2147483648,32=-2147483648,overflow=0 ```
上一篇:
C 0.1 数据类型
下一篇:
C 0.12 寄存器变量
文档导航