1.类型的基本归类
类型主要分为四大类,内置类型,自定义类型(构造类型),指针类型和空类型(void)
1.内置类型
内置类型又分为整形和浮点型
整形(分为有符号和无符号)
char
unsigned char [signed] char
short
unsigned short [int] [signed] short [int]
int
unsigned int [signed] int
long
unsigned-long [int] [signed] long [int]
[ ]框起来的表示在实际操作中常常被省略的部分
浮点型
单精度浮点型——float
双精度浮点型——double
2.自定义类型(构造类型)
自定义类型(构造类型)有很多类型,如下
a. 数组类型
b. 结构体类型struct
c. 枚举类型enum
d. i联合类型union
3.指针类型
int*
float*
double*
void*
char*
4.空类型
空类型是一种特殊的类型
void表示空类型(无类型)
通常应用于函数的返回类型、函数的参数。指针类型。
void的使用方法,如下图
当test()括号中有一个void时,表示test函数不需要传递参数,若传递参数,如下面的test(100),系统就会报错。相应的,如果test()括号里没有void,main中的test(100),也会使test正常工作,不会报错,这是C语言一直存在的一个模糊问题。
2.数据的存储
1.整形的存储
正数,负数都是按补码形式存放,原因如下
正数的原反补码相同 。
那么负数的补码如何转换呢,首先要把它转换成反码,方法是除了符号位外其他为取反,也就是0变1,1变0,如何在给反码加1就转换成了补码,下面举个例子:
有一个int型十进制数-5,原码就是把它转换成二进制,也就是
10000000 00000000 00000000 00000101
最高位为符号位,为负数时为1,为正数时为0。除符号位取反得到反码为
11111111 11111111 11111111 11111010
再给反码加1得到补码
11111111 11111111 11111111 11111011
这就是负数补码的转化过程
为什么说补码可以让加法和减法统一处理呢
看例子:如int a=1;int b=1;如何实现a-b呢
由于CPU只有加法器,a-b转化为a+(-b)
若按原码,则是00000000000000000000000000000001 与
10000000000000000000000000000001 相加
答案为 10000000000000000000000000000010 这不是正确答案
但是按照补码则是 00000000 000000000000000000000001 与
1111 1111 1111 1111 1111 1111 1111 1111相加
答案为 1 0000 0000 0000 0000 0000 0000 0000 0000
显然这里有33个二进制为,发生数据溢出,而数据溢出保留低位,只留下了
0000000000000000000000000000000
答案为0
所以转化为补码形式可以使加减法统一处理
整形的值在内存中的存储方式
计算机通常有上图这两种存储数据的方式
如上图采用的就是小端存储方式,注意在右图内存窗口中,左边为地址低位,右边为地址高位。
之所以右图看起来很违和,是因为我们讨论的是字节的顺序,而不是二进制位的顺序,int 的四个字节中从高位到低位分别为 00 00 00 14
按照小端存储方式,14先放在内存低位,再依次放入00 00 00,就形成了
14 00 00 00 的存储模式
大小端存在的原因,如下图:
2.不同类型的取值范围
以unsigned char 和char 为例
char
如上图,正数的最大值011111111很容易理解
但是负数的最小值比较难理解
存在疑惑的地方就是为什么100000000是-128
其实系统识别到100000000就会直接认为他是-128,原因如下
unsigned char
无符号为,最大值为11111111,即255,最小值为0.
3.浮点数的存储
浮点数的存储较为复杂,首先我们要按照规定对它进行拆分为
(-1)^S*M*2^E (2是由于转化为二进制)
以 5.5为例,5.5可以表示为二进制为101.1,所以拆分为
(-1)^0 * 1.011 * 2^2
因此在 5.5 中,S为 0,M为 1.011,E为 2
S的存储
在内存中进行存储时,首先应该存储S,其实S可以看做浮点数的符号位
S=0时为正数,S=1时为负数,且S只有这两个可能数。
E的存储
从上表可以看到,在 float 和 double 中S都只占一个bit位
接下来存储的是E,存入E之前必须把它加上一个数,float中是127
double中是1023,有意思的是float中E占8个bit位 而127二进制为7个1
即(1111111),只要加1就是8个bit位,所以我认为加127,是为了满足8个bit的存储方式, 而double中E占11个bit位,1023恰巧为10个1(1111111111)
讲到这里,我们继续E的存储,由于E=2,
若为float,存储时E=129,为10000001
若为double,存储时E=1025,为10000000001
M的存储
再来说M的存储方式,由于是二进制计算的M,所以M的首位一定为1,所以为了节省一位有效数字,将首位1省略,取出时会自动添上这个1
继续5.5的存储,5.5的M为1.011,省略后为011,由于本来就是二进制,所以直接存储
但是M占据的bit位有点长,在float为23位,在double为52位
所以float中为 011 0000 0000 0000 0000 0000
在double中为
011 0000000000000000000000000000000000000000000000000
最后的拼装
最后把他们拼装起来,5.5的存储方式为
float:0100 0000 1011 0000 0000 0000 0000 0000
double:01000000 00010110 00000000 00000000 00000000 00000000 00000000 00000000
4.浮点数的取出
浮点数的存储较为麻烦,那么浮点数的取出也存在一些情况
问题主要在E的取出,因为它是要减127或者1023的
E的取出有三种情况
1.不为全0全1——正常情况
2.为全0
3.为全1
解决方法如下
E为全0时,那么原来的E为-127,那么这个数非常非常小,非常接近0
这个时候我们还原M不再添上原来的1,而是直接表示成0.xxx
然后E这个时候为float(1-127),double(1-1023),
举一个例子,0 00000000 01100000000000000000000
这个数为float 直接取出M的0.11
答案为0.11*2^-126(符号位为0,数字为正)
E全为1时,表示一个无穷大的数,正负取决于符号位