[C语言数据存储深度解析]-内存数据搞不懂?三千字长文带你走进数据类型及其存储
MEUN
- C语言的基本数据类型
- 内置类型的基本归类
- 整型数据在内存中的存储
- 浮点型数据在内存中的存储
- 写在最后
C语言的基本数据类型
- 相信大家应该已经很熟悉C语言内置的几个基本数据类型了,这里仅简单罗列:
char------------字符数据类型
short-----------短整型
int---------------整型
long------------长整型
long long-----长整型(plus) (较老的编译器可能没有此类型)
float------------单精度浮点型
double---------双精度浮点型
- 类型存在的意义:
- 我们知道不同的数据类型会占用不同的内存空间。在使用时它们时,我们往往是按需使用,以免造成空间的浪费或溢出。
- 类型还决定着我们看待内存空间的视角
int a=2021; float A=2021.0f;
这里我们定义了两个变量,整型a和单精度浮点型A,这时存储a和A的两块空间都各自自占据着各自的“立场”,也就是说a开辟的空间中将按照整型的方式存和取,而A则是按照浮点型的方式存和取。
内置类型的基本归类
- 整型
char / unsigned char(无符号类型) /signed char(有符号类型)
short /unsigned short/signed short
int / unsigned int / signed int
long /unsigned long/signed long
long long同上.
Tips:
- short [int]是short未省略的写法,long等也一样。
- 类型前未写(un)signed时,short、long、int等类型直接默认为有符号的对应类型,而char则取决于编译器,大部分编译器都解析其为signed char.
用例深度理解signed和unsigned:
我们知道一个char类型可以存放的是1byte,8bit的大小元素。
这里我们假设此内存中存放二进制数字a1(a2):11111111(补码)
有: unsigned char a1 中无符号位,也就是二进制序列中每一位都是有效位,补码就是其原码、反码,那么a1的值就是255;
亦:signed char a2 中有符号位,首位1代表负数,经过计算,a2的值是-1。
我们更直观的观察一下:
int main()
{unsigned char a1 = 255;printf("%d\n", a1);printf("------\n");char a2 = 255;printf("%d\n", a2);return 0;
}
先找到它们在内存中的数值:(调试起来并在内存窗口中查看)
a1:
a2:
可以看到它们的值都是ff(16进制),也就是11111111.
接着看程序运行结果:
故而证明了我们上述理解是正确的,其它类型的数据据此举一反三即可,可以说完全相同。
- 浮点型
float----4byte
double----8byte(更高精度、更多占用)
- 构造类型
数组类型
结构体类型-struct
枚举类型-enum
联合类型-union
Tips:
- 数组的类型:如int arr[10],和之前其它类型一样,我们去掉我们定义的类型名(arr),剩下的就是类型,即此例类型为 int [10]
- 此段代码可以很直观的展示:
#include<stdio.h> int main() {int a = 10;int arr[10] = { 0 };printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));printf("----------\n");printf("%d\n", sizeof(arr));printf("%d\n", sizeof(int [10]));return 0; }
其结果为:
- 指针类型
int *
char*
float*
···
- 空类型
void表示空类型(无类型)
常应用于函数返回值类型、函数参数、指针类型等。
整型数据在内存中的存储
- 计算机中的符号数有三种表示方法,即原码、反码和补码。
下面我们看一看它们的概念:(点此可直接跳转)
正数和无符号数的原码、反码、补码都是相同的。
对于整型来说:数据在内存中均以补码的形式进行存储和计算
为什么呢?
这是因为在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理; 同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
- 数据存储的又一特点(以整型为例研究)
我们看这样一段代码
int main()
{int a = 10;int b = -10;return 0;
}
我们再看看其在内存中的存储:
乍一看,总感觉补码数据存放的顺序有些奇怪,似乎是反了?
我们仔细观察后发现,内存中自上而下,地址不断增加,每个地址指向的空间中的每个字节却是由高到低的顺,我们十进制为10的数字,在16进制存储时似乎本应是00 00 00 0a,但由于这里是在低地址放入了数据的低位,便反向排列,这就是小端存储模式
- 有小端,就可能有大端[/大笑],我们引入其概念:(源地址)
- 它们存在的意义是什么?
- 在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一
个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
你能否写一个程序看看你所使用的编译器是大端存储模式还是小端存储模式?日后我会将程序写出来,你可以和我作一个对比
浮点型数据在内存中的存储
浮点型的数据种类前文我已提到,这里不再过多赘述,先来看一道题目:
int main()
{int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
先想一想,在脑海中给自己一个答案,然后继续往下看~
这是多年前一家公司的一道笔试题目,第一次做它的时候我也做错了,故而觉得其放在此处很有代表性,它的答案是:
你做对了吗?为什么结果如此呢?接下来我将解释浮点型数据在内存中的存储方式,若你耐心看完,倘若现在没有头绪的话,到时再回头看此题就会豁然开朗。
我们知道num和*pFloat在内存中表示的是一个数字,但解读的结果为何如此之大?我们需要知道浮点数在计算机内部的表示方法。
根据国际标准IEEE754,任意的一个二进制数字V可以表示成下面的形式:
- (-1) ^ S* M*2^E
- 其中(-1)^ S表示符号位,当s=0,V为正数;当S=1,V为负数。
- M表示有效数字,大于等于1,小于2。
- 2^ E表示指数位。
这里需要知道十进制的浮点数中小数点之后数字的二进制改写写法:如5.0f,写成二进制是101.0,相当于1.01*2^2。按照上面的写法:我们容易算出S=0,M=1.01,E=2。
IEE754规定:对于32位单精度浮点数,最高位1位是符号位S,接着的8位数是指数E,剩下的23位为有效数字M。
ps:对于64位,s仍为1bit,E为11bit,M则是52bit。
- 接下来是有效位E和M的存储方式:
首先是M:
IEEE 754对有效数字M和指数E有一些特别规定。
由于1<=M<=2,即M为1.xxxxxx。在规定中,保存M时,我们总是将第一位舍去(因为它默认是1),只保存 .xxxxxxx,在我们读取的时候再将其加上,这样做多保存一位有效数字。
然后是E:
E是一个无符号数,当E占8bit时,它的取值范围是0~255 ,当其占11bit时,它的取值范围是0~2047。我们h知道,科学计数法中的E可以出现负数,故IEEE 754规定E存入时必须在其真实值上加一个中间数,对于8位的E,加上127,对于11位,则加上1023。
比如2^10的E为10,所以保存为32位的浮点数时,必须保存成10+127=137,也就是10001001.
这里E在内存中取出还可以再分成三种情况:
- E不全为0或不全为1
指数E的计算值减去127(1023),得到真实值后再将M加上第一位的i。- E全为0
此时有效数字M不再加1,而是直接还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0很小的数字。- E全为1
此时如果有效数字M全为0,则表示正(负)无穷大,这取决于S是什么。
- 我们回到这一小节一开始的那个问题:
我们先写出9的补码:00000000000000000000000000001001
- 1.以int类型存入,以%d打印,毋庸置疑第一个打印结果是9;
- 2.n由pFloat维护,pFloat的存在使得它把内存中9的补码认作浮点数序列,
那么9的补码被理解为:
0 00000000 00000000000000000001001
即是:E全0,则E直接为1-127=-126。M也只拿小数点后的数字且不加1,M为0.00000000000000000001001。再根据标准还原出数字为:
0.00000000000000000001001*2^(-126) 这个数字非常非常小,无限接近于0,又知道%f只能打印6位,那么容易知道结果是0.000000- 3 这一次是以浮点数的视角进行存储,那么它在内存中就是:
1001.0(前面省略),即是(-1)^ 0 * 1.001*2^3
E就是3然后再加上127,再写成8位二进制序列,S是0,M是1.001000……
最后的序列就是:
0 10000010 00100000000000000000000
这个时候我们又以%d的形式打印,我们就认为内存里是个有符号整数,这个序列自动会被存取为整数序列而不再是浮点数序列,这也是本文得到重点,再次出现。那么它可以直接用计算器得到答案是:1091567616- 4 和1是如出一辙,答案自然是9.000000
写在最后
本文可能唯一有理解难度的地方就是浮点数数据相关的重点在于数据在内存中的存和取,内存对于其存的二进制序列如何判断和使用,再根据IEEE754标准进行转变。这也是本文的重点,希望能对你有所帮助。
[C语言数据存储深度解析]-内存数据搞不懂?三千字长文带你走进数据类型及其存储相关推荐
- 深度解析大数据在公安领域的应用
近一两年,大数据开始在公安等行业领域得到普及应用,除了行业自身的特殊要求外,大数据也带动了相关行业的需求发展.未来,基于大数据的行业应用会变得更加深入,更多的相关厂商也会涉及其中,大数据在公安领域的商 ...
- 【工业大数据】工业大数据层层深度解析!
我深知,工业4.0,不同于今天的2.0,或3.0,一个根本性指标是工业大数据分析模型.为了论述完整性,本篇将集中阐述工业大数据及其在工业4.0中的定位和重要性." ◎文丨廣乾草記 1 大数据 ...
- 深度解析 | 大数据面前,统计学的价值在哪里?
来源:机器学习算法与Python实战 本文约8100字,建议阅读8分钟 本文介绍了关于统计学与大数据的一些观点. 统计学对大数据的意义 很高兴有这样一个机会,我能与大家在这里做一些关于统计学与大数据的 ...
- Unity 基础 之 xml 使用 Office Excel 轻松编辑保存 xml 数据,并解析读取数据
Unity 基础 之 xml 使用 Office Excel 轻松编辑保存 xml 数据 目录 Unity 基础 之 xml 使用 Office Excel 轻松编辑保存 xml 数据 一.简单介 ...
- python解析数据包_python – 解析UDP数据包
我正在构建一个UDP服务器来解析和验证传入的UDP数据包.我能够接收和解析数据包,但标头值不是我所期望的. 这是传入数据包的结构 包ID(4个字节) 包序列(4个字节) XOR密钥(2个字节) 数据包 ...
- python分析数据包_Python解析pcap数据包
Post Views: 29,789 零.前言 历时数月,终于结束了考研初试,Blog也很长时间没有更新了,期间还是有些小伙伴来Blog看文章很是感动.以后一定会坚持更新,尽量给大家推送一些干货.这次 ...
- 把数据自动填入exe的输入框_2000余字长文讲解Excel中的“数据验证”,我收藏了...
今天的文章主要跟大家分享Excel中数据有效性(新的Excel版本叫:数据验证)的基础用法! 一.如何设置数据有效性? 步骤:选中需要设置数据有效性的单元格或者单元格区域,点击菜单栏的"数据 ...
- 资深数据大牛深度解析:大数据底层架构!
https://www.sohu.com/a/197469801_99989999?sec=wd&spm=smpc.author.fd-d.20.1553654800781yejuW6n 随着 ...
- 深度解析大数据可视化管理平台的监控功能
在上一篇的文章中已经明确说过DKM作为大快发行版DKhadoop的管理平台,它的四大功能分别是:管理功能,监控功能,诊断功能和集成功能.管理功能已经给大家列举了一些做了说明,今天就DKM平台的监控功能 ...
- C语言 文件操作 深度解析 #重点知识:文件操作函数的使用#
文章目录 前言 1. 为什么使用文件 2. 什么是文件 程序文件 数据文件 3. 文件的打开和关闭 4. 文件的顺序读写 `fgetc` `fputc` `fgets` `fputs` `fprint ...
最新文章
- aidl实现进程间通信
- 独家 | 带你认识HDFS和如何创建3个节点HDFS集群(附代码案例)
- 098~100:ORM迁移
- mysql 优化方法_Mysql的优化方法介绍
- exchange java对象,【原】Java并发程序的一个应用Exchanger的实例
- net 自定义表单的设计
- vb四则运算计算机,怎么用VB做简易的四则运算计算器
- python虚拟环境安装包在哪_ubuntu环境下python虚拟环境的安装
- pandas多列聚合
- Google 周三宣布新版Google Trend上线
- 操作系统安装教程之图解Ubuntu Server 14.04安装
- WinForm 圆形进度条CircularProgressBar
- 蓝桥杯历年真题及答案汇总整理(Java、C语言、Python)
- win10企业版下载,最新windows10企业版iso镜像官方下载
- Java Web应用程序开发-深入体验Java Web开发内幕之初步
- 关闭windows defender安全中心的方法
- 利用java的for循环画一棵圣诞树
- ACL的原理与基本ACL的配置
- 使用三目运算求三个数的最大值、最小值和中间值
- Windows7下安装Docker、下载镜像和运行OpenTsdb容器