深度剖析数据在内存中的存储

1,数据类型介绍

c语言类型分为两种:内置类型 ,自定义类型(构造类型)

前面我们已经学习了基本的内置类型

char     //字符数据类型

short   //短整型

int       //整型

long    //长整型

long long //更长的整型

float     //单精度浮点数

double  //双精度浮点数


类型的意义:

1,使用这个类型开辟内存空间的大小(大小决定了使用范围)

2,如何看待内存空间的视角

整型家族:(范围可在limits.h中查到)

char

      unsigned char(无符号,没有正负之分)0--255

      signed char     (有符号)                -128--127

注意:有符号的10000000表示-128

short

       unsigned short[int]   //[]表示这个[int]可以省略

       signed char[int]

int

      unsigned int

      signed int

long

      unsigned long[int]

      signed long[int]

浮点型家族:(范围可在float.h中查到)

float

double

构造类型:

数组类型

结构体类型 struct

枚举类型 enum

联合类型 union

指针类型:

int *pi;

char*pc;

float*pf;

void*pv;

空类型:

void表示空类型(无类型)

通常应用于函数的返回类型,函数的参数,指针类型

c语言day14_11_05_学习

这里我们传了个100,会被提示,test加了void后就不能传参了

2,整型在内存中的存储

计算机中的有符号数(整型)有三种表现形式,即原码,反码,补码   无符号:原,反,补相同

有符号正数:原,反,补相同

三种表示方法均有符号位数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位三种表示方法各不相同。

原码

直接将二进制按照正负数的形式翻译成二进制就可以

反码

将原码的符号位不变,其他位依次按位取反就可以得到了

补码

反码加一就得到补码


注意:对于整型来说:数据存放内存中其实存放的是补码



int main()

{

   int a=20; //4个字节,32bit

//00000000 00000000 00000000 00010100原码

//00000000 00000000 00000000 00010100反码

//00000000 00000000 00000000 00010100补码

//转换十六进制,四个二进制位转换成一个16进制位

//0x00 00 00 14

   int b=-10;

//10000000 00000000 00000000 00001010原码

//11111111 11111111 11111111 11110101反码。。。原码符号位不变,其他按位取反得到

//11111111 11111111 11111111 11110110补码。。。反码加一得到

//转换十六进制,4个1111就是15,15写成F

//0xFF FF FF F6



整数

1,有符号数  正数:原反补相同

                    负数:原反补不同,要进行计算

2,无符号数  原反补相同


3,大端,小端介绍

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;

小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的低地址中。

0x11 22 33 44

44是低位


低地址                                                                                                                                      高地址


倒着存的是小端存储模式44 33 22 11


小练习1:判断当前机器是小端存储还是大端存储

c语言day14_11_05_学习_02


更独立的代码:

c语言day14_11_05_学习_03

小练习2:下面这段代码输出什么?

c语言day14_11_05_学习_04


小练习3:下面代码输出结果是什么?

c语言day14_11_05_学习_05



Sleep(100);//表示暂停100ms,也就是0.1秒,使用这个函数需要引用头文件#include<windows.h>

小练习4:下列代码输出结果为?

c语言day14_11_05_学习_06

注意:strlen计算的是字符串长度,当他检测到\0时停止

sizeof是运算符,求某种类型需要占用的字节数


4,浮点型在内存中的存储

常见的浮点数:

3.14159     1E10 表示1.0*10^10     float    double         long double

例:

int n=9;

float*p=(float*)&n;

//以整型的形式放进去,再以整型形式拿出,打印是9

printf("n的值为:%d\n",n);       //结果为9

//以整型形式放入,以浮点型形式拿出,出现了问题

printf(“*p的值为:%f\n”,*p);      //结果为0.000 000

解析:因为9是 00000000 00000000 00000000 00001001补码

准备打印了,却把9看成了浮点型,那么浮点型就是

0 00000000 000000000000000001001 中间的E全为0,表示无限接近于0,所以会打印 0.000 000

*p=9.0;

//以浮点型放入,以整型拿出,出现了问题

printf(“n的值为:%d\n”,n);      //结果为1091567616

解析:浮点数9.0

1001.0

(-1)^0* 1.001*2^3

0 10000010 00100000000000000000000

以整型打印,就把这个补码变成十进制在打印了,变成了1091567616

//以浮点型放入,以浮点型拿出,打印是9.000 000

printf(“*p的值为:%f\n”,*p); //  结果为9.000 000

c语言day14_11_05_学习_07


那么,浮点数怎么存储呢?


根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:

  • (-1)^S*M*2^E
  • (-1)^S表示符号位,当s等于0,V为整数;当s等于1,V为负数
  • M表示有效数字,大于等于1,小于2
  • 2^E表示指数位

举例来说:十进制的5.0,写成二进制是101.0,相当于1.01*2^2。那么,按照上面V的格式,可以得出s=0,M=1.01 ,E=2。

十进制的-5.0,写成二进制是-101.0,相当于-1.01*2^2。那么,s=1,M=1.01,E=2。

IEEE 754规定:对于32位的浮点数,最高的1位是符号位s,接着是8位的指数E,剩下的23位为有效数字M

9.0

1001.0

(-1)^0*1.001*2^3     这个3是小数点向前移动了3位哦

(-1)^s*M     *2^E


单精度浮点数存储模型


S(1bit)

E(8bit)

M(23bit)




对于64位的浮点数,最高的1位是符号位s,接着的11位是指数E,剩下的52位为有效数字M


S(1bit)

E(11bit)

M(52bit)


因为M总是一点几,所以存的时候就把这个1不用存了,只需拿出来时补上1

至于指数E,情况就比较复杂。

首先,E为一个无符号整数( unsigned int )这意味着,如果E为8位,它的取值范围为0-255 ;如果E为11位,它的

取值范围为0- 2047.但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真

实值必须再加上一个中间数,对于8位的E,这个中间数是127 ;对于11位的E,这个中间数是1023。比如,2^10的E是10 ,所以保存成32位浮点数时,必须保存成10+ 127=137,即10001001.


例:

5.5

101.1       此时小数点后面的1表示2^(-1), 也就是0.5,如果是小数点后面第二位是1,比如 0.01表示的是2^(-2),                   是0.25

(-1)^0*1.011*2^2

s=0,      M=1.011  E=2

存的是:

0  10000001(2+127)  (M的整数1就不存了) 011 (再在后面补0,补齐32个比特位)00000000 00000000 0000

0100 0000 1011 0000 0000 0000 0000 0000 转化为十六进制得到:

40b0000      b指的是11




然后,指数E从内存中取出还可以再分成三种情况:

1,E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127 (或1023), 得到真实值,再将有效数字M前

加上第一位的1。比如: 0.5( 1/2 )的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位

则为1.0*2^(-1),其阶码为-1+127=126,表示为0111 1110,而尾数1.0去掉整数部分为0,补齐0到23位

00000000 00000000 000,则其进制表示形式为:

0 01111110 00000000 000000000 0000

2,E全为0

这时,浮点数的指数E等于1-127 (或1023)即为真实值,有效数宇M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0 ,以及接近于0的很小的数字。

3,E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s) ;