C语言数据在内存中的存储
C语言数据在内存中的存储
1. C语言的数据类型
为什么每种语言都会规定数据类型呢?这是因为数据类型决定了数据在内存中的存储方式和可以进行的操作。接下来,我们将详细探讨C语言中各种数据类型的存储方式。
(1). 数据类型分类
数据类型总的来说分为四类:内置类型、自定义类型、指针类型和空类型。
2. 整数在内存中的存储
存储方式
二进制的存储方式一共有三种:原码、反码和补码。整数是以二进制补码的形式进行存储的。
正数
正数的原码、反码和补码相同,原码就是这个数的二进制形式。
负数
- 负数的反码就是原码的符号位不变,其它位按位取反,而补码就是在反码的基础上再加1。
- 如果反码想得到原码就是反码减一。
- 如果补码想要得到原码,可以取反加一或者减一取反。
原因:CPU只有加法器,面对减法时无法计算,这时就需要将正数变为负数,进行加法计算。为了使得负数的符号位和数值位可以进行统一处理,这时就需要使用补码。
例子1:正数和负数的补码
例子2:补码转到原码
常见类型的范围大小
当我们知道了整数是如何在内存中存储的,下面我们来讨论一下有符号类型与无符号类型的差异,以及可能会犯的错误。
整形int
整形int按符号分为无符号类型和有符号类型,无符号类型就是指最高位表示值,有符号类型就是指最高位表示正负。
- 大小:都是4个字节
- 范围:有符号[-32768
32767] 无符号[04294967295(2^32-1)]
可见类型的数据是有范围的,如果在使用时不注意类型的范围随意使用就会造成错误,比如下面这个例子,循环结束的条件设置为大于等于,而i本身就是无符号的数字,那么循环就会以继续下去,这就是不注意不同类型的范围所造成的结果。
字符类型
同上,字符类型按符号分为无符号类型和有符号类型,无符号类型就是指最高位表示值,有符号类型就是指最高位表示正负。
- 大小:都是1个字节
- 范围:有符号[-128
127] 无符号[0255]
同样,如果不注意它的范围也会出现问题,就如下面的例子,无符号的char类型的范围是不可能大于255的。
可见,在使用超级大的数值或者无符号类型时,一定要注意类型自身的范围!
3. 浮点数在内存中的存储
标准
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式为:V = (−1) ^(S) * M * 2^(E),从而浮点数在二进制中只存S,M,E。
注:其实V = (−1) ^(S) * M * 2^(E)就是二进制的科学计数法。
科学计数法
科学计数法通常用于科学和工程领域,是一种有效的数学工具,它不仅使数值的表示更加简洁,在进行科学计算和数据分析时具有重要作用。
科学计数法在不同进制下都有相应的表达方式,每种进制都遵循类似的结构和原则,但在具体的数字表示上会有所不同。
十进制
- 科学计数法是一种表示比较大的数或比较小的数的方法,它通过使用指数来简化表示。
- 科学计数法通常以M*10^(E)的形式表示,其中M是一个在1到10之间的数,E是一个整数。
- 10又被称作基数,基数是指数部分的底数,由R表示。
- 这种表示方法方便地表示非常大或非常小的数,同时也便于进行计算和比较。
- 十进制的科学计数法是最常见的一种形式。例如,光速为3 × 10^8米/秒,太阳质量约为2 × 10^30千克。
二级制
二进制的科学计数法与十进制的类似,只是使用2作为基数。
- V:要存储的浮点数
- S:符号位,浮点数的正负,S为1或0
- M:尾数,用二进制下科学计数法表示时的数值,大小范围:1 <= M < 2
- E:指数,二进制下科学计数法2的次方个数
- R:基数,对于十进制数的基数则是10,对于二进制数的基数则是2
存储方式
4字节
对于占4个字节的浮点数,32个比特位,方式如下:
8字节
对于占8个字节的浮点数,64个比特位,方式如下:
- 注意因为科学计数法中的E可能为负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
- 注意为啥加的是127和1023,因为E为负数时,2^(-127)已经非常小了接近0了,就规定把-127作为E的下限,同时128也是E的上限;对于8个字节的也同理,都是人为规定的
- 注意存储的是M小数点后面数字,1是不存储在位里面的
- 如果是1xx.xxxxx的情况,向前进,变成1.xxxxxxxxxxxx的形式,E为正值
- 如果是0.xxxxxxxxx的情况,向后进位,变成1.xxxxxxxxxxxx的形式,E为负值
- 注意真实存的不是E,存的也不是M
例子
a.float n = 8.5;
第一步:将十进制的数字转换为二进制的数字
先介绍一下小数怎么转二进制,如下
我们都知道整数每一位的权重是从2的0次方依次增加,那么浮点后面的数每一位的权重以2的-1次方依次递减,那么,0.5用二进制表示为0.1,0.25就是0.01等等。
8 = 1000,0.5=1 ,那么8.5 = 1000.1【转换就是整数小数都转】
第二步:用二进制下的科学计数法表示
1000.1 = 1.0001 * 2^(3)
【1000.1小数点往左移3位也就是2的3次方】
第三步:存储
此时,S = 0;E为3,加上127,转二进制是1000 0010;M存小数点后面二进制数就是0001
就是:0 10000010 00010000000000000000000
注意0001是从前往后存,存完后剩余位置的补0!!!!!!!!!!!
b.float b = - 5.0
【负数去掉负号按照正数计算,将S赋1就可】
第一步:将十进制的数字转换为二进制的数字
5 = 0011,0 = 0,5.0 = 11.0
第二步:用二进制下的科学计数法表示
11.0= 1.10 * 2^(1)
第三步:存储
此时,S = 1;E为1,加上127,转二进制就是1000 0001;M存小数点后面二进制数就是0
就是:1 10000001 000000000000000000000000
特殊情况
上面我们讨论的都是E+127不全为0或不全为1的情况,下面我们讨论两种特殊的情况。
c.E+127全为0
- E+127全为0的时候,E=1-127,说明2^(E)这个数字是一个接近于0很小的数
- 那么就规定小数点左边为0,不进行+1的操作。
d.E+127全为1
- E+127全为1说明这个数值很大,2^(E)很大,十进制数字就是无穷大
4. 大小端字节序
当超过1个比特位,有多个比特位时,我们就会考虑这么多的位的存放顺序,哪个位在前?哪个位在后?那么就规定出了一套存序法则,就是大小端字节序,大小端字节序是指的数据在内存中存储的方式不同。
首先在这之前,我们先给大家普及一个数字的低位高位知识:
比如一个十进制数字1234,这里的4就是个位,3就是十位,2就是百位,1就是千位,那么个位就是低位,依次向前十百千,千就是最高位,那么对应的一个二进制数101,1就是个位,0就是2位,1就是4位;
你猜我为啥和你说这些?就是因为大小端字节序的存储就是按照数字的低位存在高地址位和低地址位来划分的,下面我就为大家介绍大小端字节序。
大端字节序
定义
大端字节序存储是指将数据的低位存储在高地址处,而将数据的高位存储在低地址处。
大端字节序
小端字节序
定义
大端字节序存储是指将数据的低位存储在低地址处,而将数据的高位存储在高地址处。
小端字节序
例子
判断大小端字节序(VS的环境)
#include<stdio.h>
int main()
{
int a = 1;
//思路:要判断大小端只需要看它的低地址处存的是低位还是高位,地址是低地址内存单元的地址
//那么拿出它的地址访问一个字节是1则为小端字节序,0则是大端字节序
int b = *(char*)&a;
if (b == 1)
printf("小段字节序\n");
if (b == 0)
printf("大端字节序\n");
return 0;
}
本章内容结束,下章见,拜拜!!!