C语言:数据在内存中的存储

大家好,上次学习还是5月31,从今天开始,暑假学习正式开始喽!快一起叭。

这次的内容是数据在内存中的存储。

仍然是点赞+关注,追番不迷路,谢谢大家!

文章目录

1. 整数在内存中的存储

(1)整形存储的是二进制的补码

这部分的内容我们在之前已经讲解过了,具体内容在链接

整数有原码,反码,补码这三种2进制的表达形式。但是内存中存放的是补码。计算使用的是内存中的二进制,所以计算使用的就是补码。

有符号的整数,三种表示方法均有(符号位,数值位)两部分,符号位是最高位的一位:且0表示正,1表示负。其余的31位均是数值位。

  1. 正整数的原反补均相同。
  2. 负整数的原反补均不一样。
  • 原码:直接将数值 按照正负数的形式翻译成二进制得到的就是原码。
  • 反码:原码的符号位不变,其余位按位取反(~)
  • 补码:反码+1

(2) 大小端字节序和字节序判断

整数在内存中存储时非常重要的一个事情

为什么是'字节序'?因为是以字节为单位来讨论它们的顺序

地址:低--------------------->高(从左到右,低地址-->高地址)

内容:高--------------------->低(从左到右,万千百十,高字节内容-->低字节内容)

大端字节序(正着放):将一个数据的低位字节内容 存放在内存的高地址处

小端字节序(倒着放):将一个数据的高位字节内容 存放在内存的高地址处

上图展示的就是小端字节序。

(3)练习

练习一:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。

----思绪

【从简单开始,设a的值是1(a是int类型,占4个字节),它的十进制是0x00 00 00 01。如果是大端字节序(01 00 00 00,小端(00 00 00 01),只需要去拿四个字节中的第一个字节,如果是大端字节序存储,则第一个字节是1,小端则第一个字节是0。现在需要想办法拿到int类型数字的第一个字节----拿到首个字节的地址,强制类型转换成char*,之后再解引用,即可拿到第一个字节的内容】

c 复制代码
int main()
{
    int a = 1;
    if (*(char*)&a == 1)
        printf("小端字节序储存\n");
    else
        printf("大端字节序储存\n");
    return 0;
}

练习二

c 复制代码
int main()
{
    signed char a = -1;
    unsigned char b = -1;
    printf("a=%d,b=%d", a, b);
    //%d打印有符号整数,%u是打印无符号整数
    return 0;
}

-1的原码:10000000000000000000000000000001

-1的反码:1111111111111111111111111111111111110

-1的补码:1111111111111111111111111111111111111

以上写的是int类型,4个字节,4*8=32个比特位。

但是a和b是char类型,8个比特位(现在取int类型低位的8个比特位)所以a(char类型)里面放的是11111111,%d(整数32比特位)打印它的时候,需要整型提升,提升的时候需要看a是什么类型,signed提升(补符号位 ,即第一个)之后是11111...(32位)之后取反加一得数值-1。无符号unsigned提升,其余高位补0,即000...11111111,开头是0,正数,反即原,即255

此处再附送一道练习题:

c 复制代码
int main()
{
    char a = -128;
    printf("%u", a);//打印无符号整数,11...100000000,说它是无符号-->正数,补码即原码
    printf("%d", a);//打印有符号整数,开头是1,负数,取反加一
    return 0;
}

练习三

c 复制代码
int main()
{
    char a[1000];
    int i;
    for (i = 0; i < 1000; i++)
    {
        a[i] = -1 - i;
    }
    printf("%d", strlen(a));
    return 0;
}
//结果是255

此处需要注意的是,char类型的取值范围是:-128~127。此处a=128,存不下。unsigned char是0 ~255。【signed认为第一个是符号位,unsigned认为不是】

i从0开始加,加到127,再加1,则变为-128,之后继续-1,则-127,-126,-125...-1,0。在遇到0之后,strlen便不在继续统计(strlen统计\0之前的字符个数)

练习题

c 复制代码
#include<stdio.h>
unsigned char i = 0;
int main()
{
    for (i = 0; i <= 255; i++)
    {
        printf("hello world");
    }
}

在此处,i的范围是0~255,一直符合条件,for陷入死循环。

练习四

c 复制代码
#include <stdio.h>
//X86环境 ⼩端字节序
int main()
{
  int a[4] = { 1, 2, 3, 4 };
  int *ptr1 = (int *)(&a + 1);
  int *ptr2 = (int *)((int)a + 1);
  printf("%x,%x", ptr1[-1], *ptr2);
  return 0;
}

&a的类型是int* [4],在题目中将其强制类型转换成int*类型(&a+1即ptr1亦然)

a是整个数组的地址,(int)a是将地址强制类型转换成int类型。假设a的地址是0x10,(int)a+1即0x10+1(16+1),即0x11(17)【两者之间差一个字节】,再将0x11又转换成int*类型赋给ptr2

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

1.浮点数类型

浮点数包括的类型:float,double,long double

2. 浮点数公式

(1) 根据国际标准IEEE754,任意一个二进制浮点数V可以表示成下面的形式:

V = (−1) 的S次方 M ∗ 2的E次方

  • S 表示符号位,当S=0,V为正数;当S=1,V为负数
  • M 表示有效数字,1<=M<2
  • E 表示指数位

(2) 浮点数的存储,存储的就是S.M.E相关的值。

(3) IEEE 754规定:

对于32位的浮点数(float),最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

对于64位的浮点数(double),最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

3.有效数字M

对于有效数字M,有特别的规定:在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。【原因:比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的是:节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。】

4.指数E

指数E的情况较为复杂,首先,E是无符号整数(unsigned int),如果E是8位,取值范围是255;如果是11位,取值范围是1047。

但是,科学计数法中,指数是可以是负数的。比如:十进制的0.5,即二进制0.1,即

所以,IEE 754规定:存入内存时,E的真实值必须再加上中间数(对于8位的E,中间数是127;对于11位的E,中间数是1023)。假如E的值是10,存入的是10+127=137,即10001001。(这个行为可以让E变为正数)

IEE 754规定:指数E从内存中取出分为3种情况:

(1)正常情况下(即不全不是0或不全是1):E-127或1023得到真实值,再在有效数字M前面加上1。

(2)E全为0:这时,浮点数的指数(即真实的E)是-127或者-1023,1.xxxxx乘2的-127次方,这个数字无限接近于0 。==真实值E=1-127=-126,且有效值M不再加上第一位1。==还原位0.xxxxx的小数。

(3)E全为1:这时,浮点数的指数(即真实的E)是128或者,1.xxxxx乘2的128次方,这个数字接近于无穷大。

  • 以整数的形式放进去,再以整数的形式取出来---->按整数的视角来看,原反补。int n = 9; --------printf("n的值为:%d\n", n);【答案:9】
  • 以浮点数的形式放进去,以浮点数的形式取出来--->S,M,E相关的东西。*pFloat = 9.0;------- printf("*pFloat的值为:%f\n", *pFloat);【答案:9.0】
  • 以整数的形式放进去,再以浮点数的形式取出来--->整数放进到二进制序列 当成 浮点数的序列往出返,解析。int n = 9;-----printf("*pFloat的值为:%f\n", *pFloat);
  • 以浮点数的形式放进去,以整数的形式读取--->先将它弄成二进制序列,当成补码。*pFloat = 9.0;-----printf("num的值为:%d\n", n);

5.练习题

c 复制代码
#include <stdio.h>
int main()
{
    int n = 9;
    float* pFloat = (float*)&n;
    printf("*pFloat的值为:%f\n", *pFloat);
    *pFloat = 9.0;
    printf("num的值为:%d\n", n);
    return 0;
}

1. 第一部分:整------>浮点数

c 复制代码
    int n = 9;
    float* pFloat = (float*)&n;
    printf("*pFloat的值为:%f\n", *pFloat);//%f只能打印小数点后6位
    
  第一步:先将n的二进制写出来:00000000000000000000000000001001
  0      00000000     00000000000000000001001    
  S是0       E           M    
}

因为E全是0,所以E=-126,且还原为小数0.00000000000000000001001
(-1)^0 * 0.00000000000000000001001 * 2^-126 ,这个数接近于0,只打印6位,即0.000000。

2. 第二部分:浮点---->整数

c 复制代码
*pFloat = 9.0;
 printf("num的值为:%d\n", n);
 
9.0------>1001.0---->1.0010 * 2^3
正数S=0;有效数M是0010:指数E的真实值是3(存进去的是127+3=130,130是10000010),写成二进制形式
0 10000010 00000000000000010转换成整数即可1091567616
相关推荐
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹4 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
XH华4 小时前
初识C语言之二维数组(下)
c语言·算法
暮湫4 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石5 小时前
12/21java基础
java
拭心5 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
李小白665 小时前
Spring MVC(上)
java·spring·mvc