当前位置: 首页 > news >正文

C语言学习之数据在内存中的存储

        今天我们来学习以下C语言中数据是如何在内存中存储的

目录

整数在内存中的存储

大小端和字节判断

为什么会有大小端之分呢?

练习

        练习1

练习2:

练习3:

为什么char的存储范围是127到-128

练习4:

练习5:

练习6:

浮点数在内存中的存储

浮点数存储

       浮点数存储的过程

        浮点数的取出过程

        E不全为0或1(常规)

        E全为0

        E全为1

返回之前的题目解析    


整数在内存中的存储

        在之前操作符的内容中,我们就了解过整数在内存当中的存储,接下来我们来简单总结一下吧。

        整数的2进制表示方法有三种,即原码反码补码

        有符号的整数,三中表示方式均有符号位和数值位,符号位都是用0表示“正”,用1表示“负”。最高一位当做符号位,其余均为数值为。

       即:

正整数的原、反、补码均相同。

负整数的原、反、补码各不相同

原码:直接将数值位按照正负数的形式翻译成二进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反即可。

补码:反码+1就得到补码

        对于整形来说:数据存储在内存之中其实存放的是二进制的补码。因为计算机系统数值一律用补码来表示和存储,使用补码可以将符号位统一处理。同时加法和减法统一处理(CPU只有加法器)。此外,补码和原码相互转换,运算过程是相同的,不需要额外的电路。

大小端和字节判断

#include<stdio.h>
int main()
{int a=0x11223344;return 0;
}

以上这段代码经过调试后我们会发现这样一个细节,a的数据是倒着存储的 

        其实超过以恶搞字节的数据在内存中村塾的时候,就有储存顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储。

大端(存储)模式:

指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容保存在内存的低地址处。

小端(存储)模式:

指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容保存在内存的高地址处。

具体可以参考以下这个图

为什么会有大小端之分呢?

        计算机系统中我们以字节为单位,每个地质单元对应一个字节,一个字节对应8bit位。但是C语言中除了1字节的char外,还有2字节的short、4字节的int等等。对于位数大于8的处理器。由于寄存器宽度大于一个字节,必然存在着一个如何将多个字节安排的问题。因此导致了大端存储模式和小段存储模式。

        我们常用的X86和X64是小端模式,而KEIL、C51 是大端。很多ARM、DSP都是小端字节序模式。某些ARM可以由硬件决定小端还是大端。 

练习

        练习1

        设计一个小程序判断当前机器的字节序:

        思路:  取出存储数据的第一个字节。以1 为例,若取出的第一个字节为1则为1则为小端,反之则为大端

        取这个字节可以用char*的指针来判断

方法一:

#include<stdio.h>
int check_sys()
{int i = 1;return (*(char*)&i);
}

 方法二:

int check_sys()
{union{int i;char c;}un;un.i = 1;return un.c;
}
int main()
{int sys = check_sys();if (sys == 1){printf("小端\n");}else{printf("大端\n");}return 0;
}

练习2:

#include<stdio.h>
int main()
{char a = -1;signed char b = -1;unsigned char c = 255;printf("a=%d,b=%d,c=%d\n",a,b,c);return 0;
}

结果为:

为什么会是这个结果呢?

char是否由符号取决于编译器

VS上char是有符号的。

-1的原码为:

1000000000000000000000000000001

-1的补码为:

11111111111111111111111111111111111

a存储的为:

11111111

在VS上,b的存储与a一样

由char转换为int打印需要整型提升

(整型提升的知识可以参考往期博客:C语言学习之操作符-CSDN博客)

a、b整型提升后为:

11111111111111111111111111111111111

c因为是无符号数,所以为:

0000000000000000000000111111111

练习3:

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

结果为:

-128的原码

100000000000000000000000010000000

-128的补码:

1111111111111111111111111111110000000

整型提升后:

1111111111111111111111111111110000000

因此打印出来后上图的结果

为什么char的存储范围是127到-128

练习4:

#include<stdio.h>
int main()
{char a[1000];for (int i = 0;i < 1000;i++){a[i] = 1 - i;}printf("%d " ,strlen(a));//strlen求得是字符串的长度,是\0之前的字符个数return 0;
}

结果为:

为什么呢?

相当于数值从-1到-128,然后跳到了0加到了127进行一次循环 。遇到0就是相当于’\0‘。

strlen函数计算的是到'\0'之前字符的个数

练习5:

#include<stdio.h>
unsigned char i = 0;
int main()
{for (i = 0;i <= 255;i++){printf("hello world \n");}return 0;
}

结果为:无限循环打印hello world

int main()
{unsigned char i = 0;for (i = 9;i >=0;i--){printf("%u \n");}return 0;
}

无限循环打印 

练习6:

#include<stdio.h>
//X86环境,小端字节序
int main()
{int a[4] = {1,2,3,4};int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);//%x以16进制的形式打印数据printf("%x\n%x\n",ptr1[-1], ptr2);return 0;
}

结果为:

思路图如下:

浮点数在内存中的存储

        常见的浮点数:3.1415、1E10等等。浮点数包括:float、double、long double类型

        浮点数表示范围在:float.h中定义      

#include<stdio.h>
int main()
{int n = 9;float* pf = (float*)&n;printf("n的数值为:%d\n",n);printf("pf的数值为:%f\n", *pf);*pf=9.0;printf("n的数值为:%d\n",n);printf("pf的数值为:%f\n", *pf);return 0;
}

结果为:

浮点数存储

        在上面的代码中为什么n和*pf在内存中是一个数为什么浮点数和整数的差距这么大呢?这就需要我们去了解浮点数的存储原理

        在国际标准IEEE(电气和电子工程协会)中,任意一个二进制浮点数V可以表示为以下形式:

        举例说明:十进制的5.0写成二进制是101.0,相当于1.01x2^2

        那么按照上面的格式,可以得出S=0,M=1.01,E=2

        标准规定:

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

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

  

       浮点数存储的过程

        标准对有效数字M和指数E由一些特别规定

        1<=M<2,也就是说M写成1.xxxxxxx

        标准规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的xxxxxx部分。⽐如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的⽬的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保存24位有效数字。

        E相对来说比较复杂

        E是一个无符号整数。

        因此,如果E是8位的数字,范围是0到255.如果是11位,则为0到2047。

        但是E是可以出现负数的。因此标准规定,E的真实值必须加上一个中间数。

        对于8位数的E,中间数是127;对于11位数的E,这个数是1023.比如2^10的E是10.因此保存成32位的时候必须保存为10+127,即10001001

        浮点数的取出过程

        E分为3钟情况

        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位0.则二进制表示形式

1 0 011110 00000000000000000000

        E全为0

        E为1-127(或者1-1023)为真实值。M不再加上第一位的1,而还是还原为0.xxxxxxx的小数

        未来表示+-0或者接近0的数字

0 00000000 001000000000000000000000

        E全为1

        M全为0,表示无穷大(正负取决于符号位)

0 11111111 0010000000000000000000000

返回之前的题目解析    

        为什么9还原为浮点数变成了0.000000?

        有如下二进制序列:

        1 0000 0000 0000 0000 0000 0000 0000 1001

        后面的指数为E=00000000 

        M为0000 0000 0000 0000 0000 1001

        由于E全是0.所以V=(-1)^0*0000 0000 0000 0000 0000 1001*2^(-126)=

        1.001*2^(-146)

        因此V是一个接近于0的正数,所以十进制为0.0000000

        为什么浮点数9.0打印出来的整数是1091567616?

        因为9.0 等于二进制1001.0,即为1.001*2^3

        所以9.0=(-1)^0*(1.001)*2^3

        所以第一位的符号位S=0,M为011后20个0.E为3+127=130,即10000010

        写成二进制为S+E+M,即

        0 10000010 001 00000000000000000000

        

        本篇博客到这里结束了,求一个点赞,谢谢

        封面图自取:

        

        

         

相关文章:

  • ModbusRTU转profibusDP网关与RAC400控制器06功能码的应用
  • Level1.7列表
  • Java IO流学习指南:从小白到入门
  • Java程序员学从0学AI(三)
  • 【信息系统项目管理师】一文掌握高项常考题型-项目进度类计算
  • python数据结构-列表详解
  • C++:共享指针unique_ptr的理解与应用
  • C++:虚函数与纯虚函数
  • SpringAI核心
  • Pr -- 耳机没有Pr输出的声音
  • 对比Redis与向量数据库(如Milvus)在AI中的应用
  • 6.3.2图的深度优先遍历
  • 配置tomcat时,无法部署工件该怎么办?
  • linux线程同步与互斥
  • 算法笔记·数学·最大公约数
  • 酷柚易汛ERP标签打印解决方案
  • [原创]X86C++反汇编01.IDA和提取签名
  • 为什么要使用线程池
  • 使用Python控制Arduino——入门与实战
  • IEC 60034-30-1标准解析:旋转电机能效分级与全球影响
  • 计算机编程培训班/优化培训课程
  • 企业网站php模板下载/网络营销渠道有哪些
  • 南皮县网站建设/比较成功的网络营销案例
  • 成都工业学院文献检索在哪个网站做/俄罗斯搜索引擎yandex推广
  • 网站分页效果/百度人工服务
  • 企业网站报价方案下载/广州最新疫情