• 数据在内存中的存储(一个新手的理解)


     

    目录

    1.整数在内存中的存储

    2.大小端字节序和字节序判断

    2.1什么是大小端?

    2.2为什么有大小端呢?

    2.3那怎么知道编译器是什么存储呢?

    2.4几个有趣的小练习 

    3.浮点数在内存中的存储

    3.1练习

    3.2浮点数的储存 

    3.2.1 浮点数存的过程

    3.2.2浮点数取的过程

    1.E不全为0或1.

    E全为0

    E全为1


    1.整数在内存中的存储

      正整数的原,补,反码都相同。

      负整数的三种表示方法各不相同。

    提示:负数的反码等于原码符号位不变,其他位置的二进制位取反。

               负数的补码等于反码+1.

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

      为什么呢?

      使⽤补码,可以将符号位和数值域统⼀ 处理;

    2.大小端字节序和字节序判断

    首先看一个代码(VS系统)

    1. #include
    2. int main()
    3. {
    4. int a = 0x11223344;
    5. return 0;
    6. }

     这个代码的结果呢?

    可以看出低位的数字存储在了前面;这是小端存储

    2.1什么是大小端?

    超过一个字节的数据在内存中存储时,就有存储顺序的问题,按照不同的存储顺序,我们可以把分为大端字节存储和小端字节存储。在不同的编译器中不同。

    大端存储:通俗来说就是数字的比较大的位数存到了前面。就像 0x11223344,如果为大端存储的话那在内存里面 先存储的就是 11 22 33 44,和上面的图片相反

    小端存储:就是数字的比较小的位数存到了前面。和上图一样!!!

    一个自编小口诀,前大大,前小小。就是前面如果是数字位数大的数字的话就是大端存储,反之相同。

    2.2为什么有大小端呢?

    每个地址单元都 对应着⼀个字节,⼀个字节为8 bit 位,但是在C语⾔中除了8 bit 的 char 之外,还有16 bit 的 short 型,32 bit 的 long 型(要看具体的编译器),另外,对于位数⼤于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度⼤于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存储模式。

    2.3那怎么知道编译器是什么存储呢?

    可以写一个小代码来研究

    1. #include
    2. int check_sys()
    3. {
    4. int i = 1;
    5. return (*(char *)&i);
    6. }
    7. int main()
    8. {
    9. int ret = check_sys();
    10. if(ret == 1)
    11. {
    12. printf("⼩端\n");
    13. }
    14. else
    15. {
    16. printf("⼤端\n");
    17. }
    18. return 0;
    19. }

     这个代码这样看,在最上面的check 函数中先给 int i = 1,之后返回时将 i 的 强制转换为 char * 是因为 本来 存储的是 int 变量 占了4个字节 ,如果把这个 存储的 字节 单独拿出来看呢, 如果 前面 第一个 字节存的 是 01,那就代表了 将小位的数字存在了前面,为小端存储,反之相同。

    2.4几个有趣的小练习 

    1. #include
    2. int main()
    3. {
    4. char a= -1;
    5. signed char b=-1;
    6. unsigned char c=-1;
    7. printf("a=%d,b=%d,c=%d",a,b,c);
    8. return 0;
    9. }

    输出结果a b = -1,c =255.

    这个练习,可以看出用char 来存储数字,但存储数字的时候存的是补码。因为char 其实是signed char的缩写。所以a b 其实是相同的。因为 sighed char 是有符合的字符型,只保存一个字节,所以 3个变量的二进制位都是 11111111.因为前两个变量是有符号的,所以在下面要求打印%d 时发生整型提升,因为两个变量是有符号的,所以提升时前面的符号位为1.所以提升时32个二进制位全为1 。但打印时 是以原码打印,因为前两个变量的符号位是1 是负数。负数的原码 等于 补码减一 并取反。所以最终前两个变量的 原码就是 第一位二进制位和最后一位二进制位为1,其他全为0,所以等于-1.

    但不同的是 unsighed char 因为 是无符号整型,在整形提升时,c变量的二进制位前面全部补0.所以c 的二进制表示是 00000000 00000000 00000000 11111111.因为正数的原反补码相同所以,c = 255.

    1. #include
    2. int main()
    3. {
    4. char a = -128;
    5. printf("%u\n",a);
    6. return 0;
    7. }
    8. #include
    9. int main()
    10. {
    11. char a = 128;
    12. printf("%u\n",a);
    13. return 0;
    14. }

    第一个a 打印出来一个特别大的数字,第二个char

    这个练习要求打印 %u 就是无符号整型。给的两个有符号的 char 类型。

    第一个 char 因为是负的-128,所以他的 原码其实就是 10000000,对他整型提升后 就是。

    11111111 11111111 11111111 10000000,因为要打印无符号整型,所以第一个二进制位不看做符号位。,所以这个数字是正数,原码反码补码相同,所以打印出来一个特别大的数字。

    第二个 a 其实写下来和第一个a 的原码其实是一样的,因为 char 只有 一个字节8个二进制位。-128 和 128的二进制位其实相同,所以两个数字的结果相同。

    1. #include
    2. int main()
    3. {
    4. char a[1000];
    5. int i;
    6. for(i=0; i<1000; i++)
    7. {
    8. a[i] = -1-i;
    9. }
    10. printf("%d",strlen(a));
    11. return 0;
    12. }

     这个代码的最终结果是255.

    为什么呢?首先给了一个 1000的i 循环。将a【i】 = -1 - i;

    因为a 【i】是char 类型 只有 1个字节 8 个二进制位。第一个a【i】 = -1.二进制位是

    10000001.每次都减一直到 11111111 ,它的原码就是 10000000.在vs 中这个数字为-128

    所以-128再减一 就变成了 127 在一直减到 00000000.其中总共走了 255次。出现 0 。

    strlen 计算字符直到出现 0.所以得出255.

    1. #include
    2. unsigned char i = 0;
    3. int main()
    4. {
    5. for(i = 0;i<=255;i++)
    6. {
    7. printf("hello world\n");
    8. }
    9. return 0;
    10. }

    无符号char型的范围如上图。

    本题的无符号char 类型 的范围 0 -255.因为for 循环一直打印到255.但255加一 又 等于 0.

    这就会导致死循环。

    1. #include
    2. int main()
    3. {
    4. unsigned int i;
    5. for (i = 9; i >= 0; i--)
    6. {
    7. printf("%u\n", i);
    8. }
    9. return 0;
    10. }

    这题也是一个死循环,因为是无符号整型,所以当减到0时,再减一,就变成了 32个二进制位都是 1.这样一直循环下去。 

    3.浮点数在内存中的存储

    常⻅的浮点数:3.14159、1E10等,浮点数家族包括: float double long double 类型。
    浮点数表⽰的范围:float.h中定义

    3.1练习

    1. int main()
    2. {
    3. int n = 9;
    4. float *pFloat = (float *)&n;
    5. printf("n的值为:%d\n",n);
    6. printf("*pFloat的值为:%f\n",*pFloat);
    7. *pFloat = 9.0;
    8. printf("num的值为:%d\n",n);
    9. printf("*pFloat的值为:%f\n",*pFloat);
    10. return 0;
    11. }

    由上图可以看出浮点数和整型的存储方式是不同的

    3.2浮点数的储存 

    上图则是浮点数在计算机内部的表示方法。

    ⼗进制的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。
    对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
    对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

    3.2.1 浮点数存的过程

    M保证是 1<=M<2.

    E如果为8位取值范围就是0-255,如果为11位就是0-2047.根据IEEE的规定,需要给E中额外存入一个数字 8位E就是 127 ,11位就是 1023.⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

    3.2.2浮点数取的过程

    指数E从内存中取出还要分为三种情况。

    1.E不全为0或1.
    这时,浮点数就采⽤下⾯的规则表⽰,即指数E的计算值减去127(或1023),得到真实值,再将有效
    数字M前加上第⼀位的1。
    ⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其
    阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位
    E全为0
    这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,⽽是还
    原为0.xxxxxx的⼩数。这样做是为了表⽰±0,以及接近于0的很⼩的数字。
    E全为1
    这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s);
    好了,关于浮点数的表⽰规则,就说到这⾥。
  • 相关阅读:
    python面试题三
    【区块链 | 默克尔树】使用默克尔(Merkle)树实现NFT白名单
    软考 系统架构设计师系列知识点之设计模式(3)
    【电力系统】含电热联合系统的微电网运行优化附matlab代码和复现论文
    数据结构 ----- 堆排序
    SwiftUI简单基础知识学习
    此芯科技加入百度飞桨硬件生态共创计划,加速端侧AI生态布局
    c#中原型模式详解
    Oracle 设置密码有效期
    Linux引导过程
  • 原文地址:https://blog.csdn.net/a1275174052/article/details/133184639