在C语言的学习中,我们会接触到不同类型的数据,如字符类型char、短整型short (int)、整形int、单精度浮点型float、双精度浮点型double等等,这些不同地数据其存储方式也是有所差异的。为了更好地学习和使用C语言,对于这些数据类型存储方式的深入了解也是非常必要的。
1 数据类型的基本分类
正如上面所提到的,在C语言中存在诸多的数据类型,对于研究其存储方式,可将这些数据类型大致分为以下几类:
整形家族:
char, short (int), int, long (int), long long……
浮点数家族:
float, double……
空类型:
void……
构造类型(自定义):
结构体(struct),数组,枚举(enum),联合体(union) ……
指针类型:
char*, int*, float*……
那么接下来,主要讲解一下整形家族和浮点数家族的存储方式。
2 整形家族
2.1 原码、反码和补码
整形家族的数据在内存中是以补码的形式存储的,不同的数据类型之间的区别在于内存的大小不同。提及补码,就不得不补充另外两个相关的概念——原码和反码。
众所周知,在C语言中,是以二进制的方式来保存各个数据的。当我们将一个数字写成二进制的形式,这个二进制就代表C语言中的原码。以整形int为例:
对于有符号的数据,最高位代表符号位,即0为正,1为负;而其后的数字则代表相应二进制的数的大小的绝对值。
对于正数而言,其原码、反码和补码是一样的,这是C语言中所规定的。
而对于负数,保持符号位不变,反码等于原码按位取反,补码等于原码+1 ,例如:
那么,为什么要采用补码的方式存储呢?其中有一个非常重要的原因是,采用补码的形式可以直接进行加减运算,其结果仍然是补码的形式,而采用原码的方式则可能会出错。例如1-1,如果按原码的方式直接计算,那么结果将会变成-2,这显然是不对的!
2.2 大端字节序和小端字节序
在了解完整形家族数据的基本存储方式后,会涉及到一个存储字节序的问题。
首先,关于字节序,我们都知道一个字节等于8位,而对于一个整形变量,其含有32位的存储空间,也就是4个字节。通常,我们在查看某个地址的内容时,是以字节的方式来呈现的,如下:
一个字节有8位,因此一个字节的内容可以由2个16进制的数表示。而从左至右则表示每个字节从低地址向高地址依次排列。在此基础上,我们进一步引出大端和小端的概念:
根据定义,我所使用的处理器时采用小端字节序的方式来存储整形数据的。
3 浮点数家族
在浮点数家族中,常见且常用的两种类型时float和double类型,这两者的本质区别同样在于所占内存的带下不同,float占4个字节而都变了占8个字节。
对于浮点数类型数据的存储,其方式相对于整形而言确实要复杂一些。通常来说,一个浮点数N可以写成科学计数法的形式,即:
其中s代表符号位,取0或者1;
M代表有效数字,且 1<=M<2;
E代表阶数;
因此,要想存储一个浮点数,只需要分别表示出这三部分的内容即可。在C语言中,将浮点数的内存划分为三个区域来恩别存储这三部分不同的内容,如图所示:
最高位存储符号位s,取0 或1;
随后的8位(或11位)用于存储阶数,考虑到阶数可正可负,因此在存储时并不是直接将阶数对应的数字转换为对应的二进制的数字,而是需要先+127(或+1023)之后 ,在转换成对应的二进制数字进行存储;
剩余的23位(或52位)用于存储有效数字M,由于M的取值范围是[1,2),因此其小数点前一位肯定是1,为了提高所能存储的数字的精度,C语言中规定,在存储时只取小数点后的尾数进行存储,而在读取数据的时候,系统将会自动的补上所省略的1。
注:上面描述的括号里外的区别在于32位的float和64位的double。
而对于浮点型数据的读取,与存储方式略有不同,分为三种情况:
1 阶数不全为0或不全为1,此时按存储方式复原即可;
2 阶数为0即E=-127,其所代表的数等于0或无限接近于0,此时并不需要补回所省略的1,而是直接取后面的尾数组成数据即可;
3 阶数全为1即E=127,其所代表的数非常大,正无穷或者负无穷。
以上就上个人关于C语言中数据存储的一点看法,希望能够帮助到有需要的朋友。如有错误,也欢迎各位的批评与指正!