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

//数据类型的介绍以及所占据的空间

char //字符 1个字节

short //短整形 2个字节

int //整形 4个字节

long //长整形 4/8个字节 不同编译器不同

long long //更长的整形 8个字节

float //单精度浮点型

double //双精度浮点型
  • 类型的基本归类:        
    • 整形:char/short/int/long(都包括unsigned和signed,平时编译器默认signed)
    • 浮点型:float/double
    • 构造类型:数组/结构体(struct/枚举(enum)/联合(union)
    • 指针类型:int* pi/char* pc/float* pf/void* pv(因为没有具体值,只能用来暂时存放指针

//整形变量在内存中的存储

三种方法都有符号位和数值位,符号位用“0”表示正,用“1”表示负,数值为的表示方法不同

  1. 负数的原码、反码和补码        
    • 原码:二进制按照正负数的形式翻译
    • 补码:原码按次取反
    • 补码:反码+1
  2. 正数的原码、反码和补码:正数的三个码都相同

对于整形变量而言,数据内存中存放的是补码,用printf打印的是原码

  • 原码和补码的转换
  1. 原码—>补码:原码按位取反+1
  2. 补码—>原码:补码-1取反/补码取反+1

//地址的存放方式

地址在内存中以字节为单位进行存放 ,每个地址单元对于1个字节,1个字节为8bit

这是初始化int a = 1的地址,a地址的16进制为0x00000001

注:

  • 1个比特位为1个二进制位,在16进制的地值中换算可知:    

                        1个16进制位 = 4个2进制位(2^4 = 16)

                        1个字节 = 1个比特位 = 1个二进制位

                        2个16进制位  = 8个2进制位 = 一个字节        

  • 1个数值超过1个字节,要存储到内存中,就有顺序的问题

所以这里可以理解为:01 为一个字节,后面的6个00两两为1个字节,正好4个字节对应整形

  • 存储模式介绍:       
    • 大端:数据低位保存在内存高地址中,高位保存在低地址中
    • 小端:数据低位保存在内存低地址中,高位保存在高地址中

对于0x00000001而言,01为处于低字节的位置(用百位数的个十百理解)

在VS中,地址存放应用了小端的存储方式

//数据存储和截断

<例1>

#include <stdio.h>
//输出什么
int main()
{
    char a = -1;
    signed char b =-1;
    unsigned char c =-1;
    printf("a=%d,b=%d,c=%d",a,b,c);
    return 0;
}

在一般的编译器中,char = signed char

当定义为整形数字时,访问时发生截断,此时会产生数据的整形提升:

 最后得到a=b=-1

如若定义该数为无符号整形,那么在访问截断后发生升级时,无符号数高位补0:

 此时c=256

<例2>

#include <stdio.h>
#输出什么
int main()
{
    char a = -128;
    printf("%u\n",a);
    return 0;
}

%u打印的是无符号整形,认为内存中存放的补码对应的是一个无符号数
%d是打印有符号整形,认为内存中存放的补码对应的是一个有符号数

 对于字符类型打印数字,我们按照之前的流程进行

然后进行整形提升(char默认为有符号位,所以前面全部补上1)

此时因为要打印的是无符号整形,此时所有的1又都看成常数,所以结果得:

 注:无符号类型的数字恒大于0

<例3>

#include <stdio.h>
//程序运行结果如何
int main()
{
    unsigned int i;
    for (i =9;i>=0;i--)
    {
        printf("%u\n",i);
    }
    return 0;
}

unsigned int 为无符号整形,恒大于0,最后0-1的时候得到的补码是全1,因为是无符号整形,会被看做一个很大的正数,不会等于0

<例4>

#include <stdio.h>

int main()
{
    char a[1000];
    int i;
    for(i=0;i<1000;i++)
    {
        a[i]=-1-i;
    }
    printf("%d",strlen(a));
    return 0;
}
  • char的范围从-128~127,顺序为-1,-2,……-128,127……0,-1,-2……一直循环到1000次
  • strlen求字符串长度,只找到\0(本质为0)之前的数字就停止,所以总共找到255个数字就停止

//浮点型在内存中的存储

根据国际标准IEEE754,任意一个二进制位可以表示成以下的形式:

  • (-1)^S*M*2^E
  • (-1)^S表示符号位,当S=0,V为正数;S为1,V为负数
  • M表示有效数字,大于等于1,小于2
  • 2^E表示指数位                    
  • 例:5.5=101.1(101表示整数部分,.1为小数部分(2的-1次方))=1.011*2^2
  •         s=0;M=1.011;E=2

数据的存入:

<1>有效数字M:

  •  前面说过,1<=M<2,也就是说,M可以写成1.xxxxx的形式。
  • IEEE754中规定,在计算机内部保存时,默认这个数是第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。
  • 比如保存1.01时,只保存01,等到读取的时候,再把第一位加上去
  • 这样做的目的是为了节省一位有效数字。以32位浮点数为例子,留给M只有23位,将第一位1舍去后,等于可以保存24位有效数字。

<2>指数E:

  • E本身为无符号整数,如果E为8位,它的取值范围位0-255;E为11位,取值范围在0-2047.
  • 但科学计数法中的E是可以出现负数的,所以IEEE754规定:存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11的E,这个中间数是1023。
  • 比如2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137

数据的取出:

<1>指数E:

  • E不全为0或者不全为1:指数E的计算值-127或者1023,得到真实值,再将有效数字M前加上第一位的1

                例如:0.5表示形式为:S:0 E:01111110(-1+127)M:0000000000000000000000

  • E全为0:这时,浮点数的指数E等于1-127(1023)即为真实值,有效数字不再加上第一位的1,而是还原为0.xxxx的小数,这样是为了表示+-0,以及接近于0的很小的数字
  • E全为1:这时,有效数字M全为0,表示+-无穷大

例子:

 

 float类型只保留小数点后6位数字

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值