提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、C语言中的基本数据类型
(1)整数类型(整数即不带小数部分的数)
1.int 整型
2.short 短整型
3.long 长整型
4.long long 更长的整形
5.char 字符型
在整数类型中除了char类型外,其他类型都是在int类型的基础上增加附属关键字修饰int类型,我们通常写的整数类型并不是完整的,是经过简化的
在类型前加上unsigned表示这是一个无符号类型,而加上signed表示这是一个有符号类型,除char类型外,其他类型默认为signed(有符号类型),一般会将signed省略掉。(char类型没有规定的默认类型,根据编译器不同,有着不同的默认类型)
(2)浮点型类型(即带有小数部分的数)
1.float 单精度类型
2.double 双精度类型
3.long double 长双精度类型(不常用)
二、数据在内存方式
1.整数数值的存储
首先 整数类型的存储分为无符号类型和有符号类型存储,而有符号类型又分为正数和负数的存储,不管怎样分类整数类型在存储都要通过原码,反码,补码来完成
(1)原码
原码是将数据转换为二进制后的数据 类如一个int(有符号)类型的数据我们将起赋值为“10”后转换为原码后就对应的是二级制数00000000 00000000 00000000 00001010
首先由于int是有符号类型,(高位)第一位是符号位,1表示负,0表示正,而“10”为正数,符号位为0,转换为二级制数则为:1010,而int类型为4个字节 32个比特位,多余的数值位按位填0表示 00000000 00000000 00000000 00001010
int类型的数据赋值为“-10”,对应的原码则是10000000 00000000 00000000 00001010
无符号类型的数据则不用考虑符号位,存储时数值皆为非负数,而正数和零的原码,反码,补码相同
(2)反码
正数和零的原反补码相同,负数原码的符号位不变,数值位按位取反得到的就是反码
上面我们得到了 int类型数值为“-10”的原码10000000 00000000 00000000 00001010
将原码 符号位不变,数值位按位取反 11111111 11111111 11111111 11110101 这就是int类型数值为“-10”的补码
(3)补码
正数和零的原反补码相同,负数反码加一得到就是补码
int类型数值为“-10”的反码 11111111 11111111 11111111 11110101 加一
11111111 11111111 11111111 11110110 即得到补码
在内存储存的数都是以补数存储的,正数和零的原反补码相同,负数先将数值转换对应的二进制数得到原码,原码除符号位外,其他位按位取反得到反码,反码加一得到补码
2.整数数值怎样进行运算的
在计算机内部所有的数值都是以补数进行存储和运算,在表达时在将补码转换为原码
那么原码反码补码的意义何在呢 在进行运算时计算机内部只能进行加法运算,通过全加器完成计算,10-10的在进行运算时等于多少呢 由于计算机内部只有加法所有必须转换为加法计算,10+(-10)将其转换为二进制数计算
00000000 00000000 00000000 00001010+10000000 00000000 00000000 00001010=
10000000 00000000 00000000 00010100
我们将10+(-10)转换为原码进行运算发现得出的结果既然是-20。为什么会这样呢?
有理数减法法则:减去一个数,等于加上这个数的相反数。
10-10 == 10+(-10)这是没有问题的,10和(-10)互为相反数,相反数相加的到0。那么问题就出现在转换为原码后的运算,我们对原码进行检查发现,在十进制的数值互为相反数的数值在转化为原码后居然相加结果不为0,这说明两个原码根本不是相反数,这违背的有理数减法法则,导致计算结果出错。
如果我们把32bit位的二进制数数轴画出来会发现,这是一个循环的数轴,并且它只能做加法导致一个数在数轴上只能前进不能后退,如果我们把十进制的数轴画出来会发现10+(-10)之所以能得到0是应为,10加(-10)是10后退了10位,得到0,后退即为减去一个数,实质上任然在做减法,转换为原码后进行加法运算得到-20,是因为转化为原码这两个数并不为相反数。
那我们将他们转化为反码后呢,由于正数的原反补相同不用变化,负数的原码除符号位其他位按位取反得到反码,将反码相加会得到什么呢?
00000000 00000000 00000000 00001010+11111111 11111111 11111111 11110101=
11111111 11111111 11111111 11111111
结果为11111111 11111111 11111111 11111111,为什么的出来的会不等于0呢?这时候该轮到补数上场了,首先再这里先抛出一个概念一个数值的原码和补码互为相反数,如果我们将负数的反码加一转为补码会怎么样,
00000000 00000000 00000000 00001010+11111111 11111111 11111111 11110110=
1 00000000 00000000 00000000 00000000
为什么会转换为补码计算就正确了呢,观察二进制数轴会发现数值只能前进不能后退,从理论上说一个数值无论怎么样前进都不能回到零点,但是由于32位二进制的最大值为11111111 11111111 11111111 11111111一旦数值到达这个值再加一就会成为
1 00000000 00000000 00000000 00000000 这样的33位二进制位,这样一来计算机能读取的只有32位二进制数就变成0了。即使数值只能前进(相加)也能回到零点,这样我们就能找到数值上所有数的相反数了,一个数值减去另一个数值就是加上他的相反数这样我们就可以把所有减法转化为加法了。
三.浮点数数值的存储
首先必须声明浮点数存储和运算和整数是不相同的,原反补码只能用于整数(不带小数部分的数值),再介绍浮点数数值的存储之前先来一段C程序的代码。
循环100次,每次循环num增加0.1的数值,得到的结果应该是10
#include <stdio.h>
int main() {
float num=0;
int i;
for (i = 0; i < 100; i++) {
num += 0.1;
}
printf("%f", num);
return 0;
}
这里的得到的结果为什么会是10.000002?
想要知道计算机计算为什么会出现丢失精度就要明白浮点数是怎么存储再计算机中的。
浮点数是指用符号,尾数,基数和指数四部分组成表达的小数。
这里面并没有基数部分的存储空间是因为计算机内部自能使用二进制,基数是固定的“2”所以再存储时不必考虑基数。
我们介绍的浮点数的存储和表达方式对照IEEE标准。
符号部分:用一个位来表达,正为“0”,负为“1”;
尾数部分:将小数部分的值固定1的正则表达式;
指数部分:EXCESS系统表现
符号部分和整数的符号位相同,尾数部分是将小数部分的值固定1的正则表达式,正则表达式是“按照特定的规制来表达数据的形式”,具体就是将小数部分左移或右移,需要说明的是由于小数的整数部分经过移位固定为“1”所以再存储时整数部分不用存储,只需要将小数部分存储到尾数部分。
1011.0011 原始数值
0001.0110011 右移使整数部分为1
0001.011001100000000000000 小数部分存储到尾数部分(23位)
011001100000000000000 实际存储到尾数部分的数值
指数部分的EXCESS系统表现是什么?
由于要数值一般是要经过左移或右移才能成为正则表达式,而指数部分就是为了将数值还原到原来的数值,指数部分为正数时表示尾数部分向左移位,指数部分为负数时表示像向右移位。但是指数部分是没有符号位的那要怎么表达正数负数呢?这就要用到EXCESS系统表现了,将指数部分范围的中间值设置为0。
十进制数 0.75 用单精度浮点数来表示 0-0111 1110-1000 0000 0000 0000 0000 000
符号部分为0表示是正数,尾数根据正则表达式规制实际表达的是1. 1000 0000 0000 0000 0000 000这个二进制数,转化为十进制数后就是1.5。指数部分的0111 1110根据EXCESS系统表现的是-1,基数部分是固定的2。 +(1.5*2^-1) =+0.75
再了解浮点数的存储后我们再回到第一个问题为什么for的循环100,每次加0.1会得到10.000002?
这是因为计算机采用浮点数来处理小数,但浮点数不能表达每一个小数,类如0.1,0.33333的无限循环小数,π等小数都没法准确的表达,计算机只能做到表达他们的近似值。为了避免精度丢失一般会将小数部分转化为整数进行运算后,再将结果转换回小数这样就可以避免精度丢失了。