C语言数据存储

数据存储

注:本文档说的系统是指的作者目前使用的操作系统(Win10家庭版64位)。

在计算机中,数据以二进制的方式进行存储。在内存中,存储的最小单元为比特(bit)位,以01来表示比特位的值。

对于本系统总共有64根地址总线,每根线都有两种状态:高电位和低电位(可以通俗的理解为开关的闭合和断开)。在高电位时表示1,低电位时表示0。每根线都有2种状态,那么总共可以表示出2^64种状态,即可以存储2^64比特的值。由于使用比特表示起来比较麻烦,所以规定8个比特位为1个字节(Byte),通常把1个字节表示为1B,那么有如下的换算关系:


含义

换算

1B

1字节

8bit

1KB

1千字节(Kilobyte

1024B

1MB

1兆字节(Megabyte

1024KB

1GB

1千兆字节(Gigabyte

1024MB

1TB

1万亿字节(Trillionbyte

1024GB

1PB

1千万亿字节(Petabyte

1024TB

1EB

1百亿亿字节(Exbyte

1024PB

1ZB

10万亿亿字节(Zettabyte

1024EB

1YB

1亿亿亿字节(Yottabyte

1024ZB

1BB

1千亿亿亿字节(Brontobyte

1024YB

由上表我们可以计算得出一台64位操作系统理论上可使用的内存空间为2EB。而对于一般计算机,通常装的内存大小为8GB(也有4GB12GB16GB32GB),这些都比较常见。

为了便于管理,通常把计算机内存划分为一个个小的内存单元,一个内存单元可容纳的数据为1字节。

C语言中的数据类型和存储方式

C语言中的数据类型

C语言中包括内置数据类型和自定义数据类型。

其中内置数据类型为:字符型(char)、短整型(short)、整型(int)、长整型(long)、更长的整型(long long)、单精度浮点型(float)、双精度长整型(double)。

自定义数据类型包括:构造类型,如数组,结构体,枚举和联合类型、指针类型。

除此以外,C语言中还有空类型(void)。

C语言中数据的存储方式

除上述所说的分法外,C语言内置数据类型也可以整体分为整型类型和浮点型。即如下表所示:(其中“[]”及其里边的内容可以省略不写,例如short [int],我们通常习惯写成short

数据类型

在内存中占用字节的大小(单位:字节)

整型

char

unsigned char

1

signed char

short

unsigned short [int]

2

signed short [int]

int

unsigned int

4

signed int

long

unsigned long [int]

4

signed long int

long long

unsigned long long

8

signed long long

浮点型

float

float

4

double

double

8

虽然char为字符型,但是char在内存中存储的是对应字符的ASCII码值,所以把char也归为整型。

  关于占用字节数,不同的操作平台和编译器结果可能会不同。C语言标准有如下规定:char1字节,short不少于2字节,int不少于shortlong不少于4字节且long不少于intlong long不少于longfloat占用4字节,double占用8字节。上表中所表示的字节大小表示的是当前编译器中各类型占字节数的大小。查看当前编译器数据类型占用字节大小的方法为sizeof(类型)。如图所示

image.png

原码、反码和补码

数据在内存中以二进制存储,我们将某个数据的二进制序列称为机器码。

以整数为例,对于char类型的整数10,其对应的二进制码为1010,因为一个char8位,所以在其左侧补0,使其变为8位,即一个char类型的二进制码对应为00001010。一个数直接表示为二进制码,我们称其为原码

同理对于负数,以char类型的-10为例,其二进制码对应为10001010,其中第一位表示符号位,是-1的次方(即第一位是0,表示-1的零次方对应就是正数,第一位是1,表示-1的一次方,对应的是负数)。10001010char类型-10的原码,保持符号位不变,其他位按位取反得到11110101,我们称这个为反码

对反码加一,则得到补码

有如下规定,在计算机中,无符号数(unsigned)和正数(指的都是整型类型)的原码、反码、补码相同。计算机中对于数据的存储均采用补码(使用补码可以将符号位和数值域统一处理,此外,CPU只有加法器,使用补码对加法和减法也可以统一处理,此外补码与源码相互转换运算过程相同,不需要额外的硬件电路)。

数据存储范围(整型)

char数据类型为例来说明,其他整型的推导同理。

对于无符号charunsigned char),因为没有符号位,所以每一位都是数值位,则其能存储28次方即256个数值,所以其表示的范围为0~255.

对于有符号char,第一位为符号位,其余为数值位。00000000表示十进制0,00000001表示十进制100000010表示十进制2……01111111表示十进制的127,对011111111后变为10000000,规定该数表示-128

对于10000001,因为是有符号数,所以其存放的形式是补码,将其转换为原码:符号位不变,其他位按位取反得到11111110,再加1后得到原码,11111111,第一位是符号位,是1,表示负数,其余为数值位,数值位转换为十进制后为127。即10000001表示的为-127,……,11111111表示十进制的-1。所以对于有符号char,其表示的范围是-128~127

可以看出,对于无符号char,如果一个数据达到了该类型对应的最大值,对该数据进行+1操作,其就会变为0.有符号char,如果一个数据达到了char能表示的最大值,即127,如果对其进行+1操作,则该数据就会变为-128。同理,如果一个数据达到了有符号char表示的最小值,对其进行-1操作,该数据就会变为127。所以可以分析得出如下结论:对于整型数据类型,如果某个数据达到了对应数据类型的最大值,对其进行+1操作,那么该数据就会变成其对应数据类型的最小值。

在此操作系统中,其他整型的数据表示范围如下表所示

数据类型

在内存中占用的字节数

表示数据范围

unsigned char

1

0~255

signed char

-128~127

unsigned short [int]

2

0~65535

signed short [int]

-32768~32767

unsigned int

4

0~4294967295

signed int

-2147483648~2147483647

unsigned long [int]

4

0~4294967295

signed long int

-2147483648~2147483647

unsigned long long

8

0~2^64-1

signed long long

-2^63~2^63


数据在内存中的存储方式(整型)

大端存储:数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中。

小端存储:数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中。

 

如何判断当前操作系统的存储方式?

思路:对于int类型的1,在内存中占用了4个字节,只用判断第1个字节的值,(指针指向的第一个字节位置对应的是低地址)使用char类型的指针读取1在内存中第1个字节对应的值,如果系统是大端存储,char类型读出的结果为01的高位是0),如果系统是小端存储,读出的结果是1.其代码如下:

//返回0,是大端存储,返回1,是小端存储
int check_sys(void)
{
    int i = 1;
    return *((char*)&i);
}
int main(void)
{
    if (check_sys() == 1)
    {
        printf("小端存储");
    }
    else
    {
        printf("大端存储");
    }
    return 0;
}

运行程序后发现,当前操作系统为小端存储模式。

浮点型数据在内存中的存储

浮点数家族包括floatdoublelong double(不常用)类型。根据国际标准IEEE754标准,任意一个二进制的浮点数V可以表示成下面的形式:

V=(-1)^(S) * M *2^(E)

(-1)^(S)表示符号位,S=0V为整数,S=1V为负数;

M表示有效数字,其取值范围大于等于1,小于2(因为是二进制,所以不会有2);

2^E表示指数位。

以十进制的5.0为例,其二进制为101.0,即1.010*2^2,可以看出,此时S=0,M=1.01,E=2.

对于十进制的-5.0,其二进制为-1.010*2^2,可以看出,此时S=1,M=1.01,E=2.

IEEE 754有如下规定:

对于32位浮点数,最高位存放符号位S,接下来的8位存放指数E,剩余的23位存放有效数字M

对于64位浮点数,最高位存放符号位S,接下来的11位存放指数E,剩余的52位存放有效数字M

M位从左至右表示为2^(-1),2^(-2)…如图所示

image.png

image.png

对于有效数字M,其范围总是大于等于1,小于2,因此IEEE 754规定在计算机内部只保存小数点后边的部分,等到读取数据时,再把“1.”加上。例如保存1.01时,只保存01,读取的时候把“1.”加上,变为1.01。这样做的目的是可以多保存一位有效数字,使得精度提高。

对于指数EE为一个无符号整数,意味着如果E8位,那E得到取值范围是0~255E11位,取值范围为0~2047.但是在实际中,指数可以出现负数,因此IEEE 754规定,存入内存时,E的真实值必须加上一个中间数,对于8位的E,该数为127,对于11位的E,该数为1023.例如2^10E10,所以其保存为32位浮点数时,应保存为10+127=137,对应的二进制码为10001001

从内存中读取浮点数时,又分为如下几种情况:

(1).E不全为0或不全为1,将E的计算值减去中间数(8位的E127,11位的是1023)后得到E的真实值,将有效数字M前加上“1.”即可得到浮点数的值。例如0.5的二进制形式为0.1,即1.0*2^(-1),S=0,E=-1,M=1.0,以32位浮点存储为例:E-1+127=126,对应的二进制为01111110,所以0.5在内存中按如下方式存储:0 01111110 00000000000000000000000。同理,对于1 01111110 10000000000000000000000,从内存中读取时,第一位为1,所以S=1,接下来八位不全为0,也不全为1,其二进制对应的十进制是126,所以E=126-127=-1,在剩下的23位左边补上“1.,所以M=1.1,所以最终对应的浮点数应为V=(-1)^(1)*1.1*2^(-1),转化为十进制为-0.75.

(2).E全为0,浮点数的指数E等于1-127(或者1-1023)即为真实值,此时有效数字M左边不再加“1.,而是直接还原为0.××××××。这样做是为了表示±0以及接近于0的很小的数字。

(3).E全为1,如果有效数字M全为0,表示正负无穷大。其中正负由S决定。


猜你喜欢

转载自blog.51cto.com/14914896/2540055