1. C程序编译
C程序的编译可以简单分为4个步骤:预处理,编译,汇编,。
- 预处理 展开头文件·删除注释,条件编译(例如#if,#ifdef),宏定义展开 (.i文件) gcc -E ex.c -o ex.i
- 编译 检查语法,将高级语言转换成汇编语言(.s文件) gcc -S ex.i -o ex.s
- 汇编 将汇编语言转化为机器语言(.o文件) gcc -c ex.s -o ex.o
- 链接 链接不同系统下的库文件(可执行文件) gcc ex.o ex
2. 原码、反码、补码
2.1 基础概念
- 数据在技术及内部是以补码的形式存储的
- 数据分为有符号数和无符号数。
- 正数的首位地址为0,其原码是有十进制转二进制
- 负数的首位地址为1,其原码是十进制数转二进制数,再讲首位地址改为1。
- 对于一个数字,技术及要使用一定的编码方式进行存储,原码、反码、补码是机器存储一个具体数字编码方式。
2.2 具体方式
原码
符号位(首位)加上真值得绝对值。
int 12的原码--->0000 0000 0000 0000 0000 0000 0000 1100(补全所有位)
int -12的原码--->1000 0000 0000 0000 0000 0000 0000 1100
反码
正数的反码与原码相同,负数的反码是除符号位,其余位取反。
int 12的反码--->0000 0000 0000 0000 0000 0000 0000 1100(补全所有位)
int -12的反码--->1111 1111 1111 1111 1111 1111 1111 0011
补码
正数的补码与原码相同,负数的补码是反码基础上加1
int 12的反码--->0000 0000 0000 0000 0000 0000 0000 1100(补全所有位)
int -12的反码--->1111 1111 1111 1111 1111 1111 1111 0100
2.3 三种编码方式各自的用处
<font size=4 color=red> 计算机只做加法运算。</font>
- 情景1:两个正数进行运算,原码即可解决
10+2
1010 +
0010
_______________
1100----->(12)
- 情景2:一正一负,10+(-2),
//原码
10+(-2)
1010 +
1010
_______________
10100----->(20)
//结果明显是不对的,那就说明负数与正数之间采用的不是原码
//反码
10+(-2)
0000 0000 0000 0000 0000 0000 0000 1010 +
1111 1111 1111 1111 1111 1111 1111 1110
_________________________________________
1 0000 0000 0000 0000 0000 0000 0000 1000
//我们发现,好像除去符号位的话,结果是正确的。但是有一种情况还例外
10+(-10)
0000 0000 0000 0000 0000 0000 0000 1010 +
1111 1111 1111 1111 1111 1111 1111 0101
_________________________________________
1111 1111 1111 1111 1111 1111 1111 1111--->1000 0000 0000 0000 0000 0000 0000 (原码)
//除去符号位,好像结果出乎意料了啊,是-0。
//再试一下
1+(-1)
0001
1110 +
__________
1111 ----->1000(原码)
//还是-0
//虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
//于是补码的出现, 解决了0的符号以及两个编码的问题:
1+(-1)
0000 0000 0000 0000 0000 0000 0000 0001
1111 1111 1111 1111 1111 1111 1111 1111 +
____________________________________________
1 0000 0000 0000 0000 0000 0000 0000 0000--->原码(0000 0000 0000 0000 0000 0000 0000 0000)
//补码
10+(-2)
0000 0000 0000 0000 0000 0000 0000 1010 +
1111 1111 1111 1111 1111 1111 1111 1110
_________________________________________
1 0000 0000 0000 0000 0000 0000 0000 1000
//首位的1,数据溢出了,系统自动将其舍弃了。所以结果是 1000(补码)
//1000(补码) 首位是1,说明它是正数,正数的原码,补码相同,其结果转化为十进制8。
总结:根据以上的代码,可以确定的话0的话只有+0,没有-0,所谓的0不带符号,其实就是把它包含进了正数范围去了。
这样的话,假设我们现在的int是4Byte的,那么(10000 0000 0000 0000 0000 0000 0000 0000)其实是没有反码和原码的,因为你第一位表示符号,你根本无法减1,去转换反码。
-1-int的最大值(2^31-1)的补码结果就是(10000 0000 0000 0000 0000 0000 0000 0000),因为实际上是使用以前的-0的补码来表示,所以没有反码和原码的。
(10000 0000 0000 0000 0000 0000 0000 0000)其实就是(-2^31), 所以int的最小范围就是这个,而不是((-2^31)+1)。
使用补码,不仅解决了0的问题,还能多表示一个最低数。
参考: