深度剖析数据在内存中的存储——C语言进阶

news2024/11/23 11:19:24

       

目录

一、数据类型介绍

1.1 整型家族

1.2 浮点型家族

1.3 构造类型

二、整型在内存中的存储

2.1 原码、反码、补码

2.2 大小端字节序

大小端存储的定义

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

怎么判断一个当前机器的大小端

2.3 有符号和无符号的区别

三、浮点型在内存中的存储

浮点数的存储规则


        今天在这里我们将探讨数据在内存中的存储,首先会带着大家了解一下数据类型,然后探讨一下整型在数据中是怎么储存的,原码、反码、补码是什么,然后了解一下大小端字节序,为什么按照这样的形式去存储,最后我们再探讨浮点数在内存中是如何存储的。下面来跟着小编一起探讨学习吧!

一、数据类型介绍

C语言为什么有这么类型呢?而且整型又分为整型、长整型、和短整型呢?其实是为了我们更好的去限定变量,比如用短整型来限制年龄。这样使用的话,内存利用率会高一些,我们在使用的时候,根据不同的取值范围来选定不同的类型。

1.1 整型家族

char:unsigned char、signed char

short:unsigned short 、signed short

int: unsigned int、signed int

long:unsigned long、signed long

        注意:字符归为整型家族的原因:字符本质上存储的时候,在内存中存储的是ASCII码值,是整型,所以归类的时候归类到整型。

1.2 浮点型家族

float

double

long double

1.3 构造类型

>数组类型

>结构体类型

>枚举类型

>联合(共用体)类型

注意:我们前面说介绍的几种类型都是C语言自己的内置类型,本身就存在的,我们可以直接用的,当然C语言也允许我们去构造类型,也就是自定义类型。

1.4指针类型

int* pi
char* pc
folat* pf
void* pv
结构体指针

注意:void表示空类型(无类型),通常用于返回类型,函数的参数,指针类型。

二、整型在内存中的存储

        一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
那接下来我们探讨数据在所开辟内存中到底是如何存储的呢?

2.1 原码、反码、补码

        在了解整型在内存中的存储形式之前,我们先要了解学习一下三种形式,也就是整型数据的二级制表现形式:原码、反码、补码。
        这三种表示方法均由 符号位数值位两部分,最高位是符号位,符号位都是用‘0’表示正,用‘1’表示负。而数值位整数的原码反码补码都相同。
        负整数的三种表示方法各不相同
原码: 直接将数值按照正负数的形式翻译成二进制就可以得到原码。
反码: 将原码的符号位不变,其他位以此按位取反就可以得到反码。
补码: 反码+1得到的就是补码。
对于整型来说:数据存放在内存中,其实存放的是补码。这是为什么呢?
①使用补码,可以将符号位和数值位统一处理
②加法和减法也可以统一处理,(CPU只有加法器),此外,补码与原码相互转换,其运算过程是相同的,不需要额外硬件电路。

 

 

 验证我们创建的num变量在VS这个编译器上是如何存放的,在VS这个编译器上,为了方便展示,显示的是十六进制,如上图,因为我们这里设置的是32位,也就是32个比特位,32个二进制的数转换成十六进制,四个二进制位换成一个16进制位,也就转换成8位16进制的数,num的补码换成16进制的数就为0x 00 00 00 0a,num2的补码换成16进制的数就为

0x ff ff ff f6。但是存进去之后我们发现是倒着存储的,这是为什么呢?这就涉及到大小端的问题,接下来,我们来学习一下大小端字节序。

2.2 大小端字节序

大小端存储的定义

        正如刚刚说说,当我们知道,计算机存储一个整型数据,是将这个整形数据的补码存放在内存中的,但是我们发现,存储的时候是倒着存储的,这是为什么呢?这是就要学习一个概念,大端和小端

大端(存储)模式:数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;

小端(存储)模式:数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中;

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

        为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bitchar之外,还有16 bitshort 型,32 bitlong型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因 此就导致了大端存储模式和小端存储模式。

例如:一个 16bit 的 short x ,在内存中的地址为 0x0010 x 的值为 0x1122 ,那么 0x11 为 高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高 地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则 为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式 还是小端模式。

怎么判断一个当前机器的大小端

        其实我们可以通过调试的方法直接去看的。如下图

 当然我们可以写个代码(程序)去测试当前机器是大端还是小端

        假设有个int变量a,大小为1,写成16进制就是0x 00 00 00 01,那么存到内存中也就两种情况,如下图所示,仔细对比一下我们会发现,我们只要对比第一个字节的内容,是不是就能区别出来了,如果第一个字节内容是‘1’就是小端,如果第一个字节内容是‘0’就是大端。

         那么问题来了,如何拿到第一个字节的内容呢,其实也很简单,我们取地址a拿到的就是a的地址,a占四个字节,每一个字节都有地址,取地址a其实拿到的是a第一个字节的地址,类型是int*,如果对int*类型数据解引用拿到的是四个字节,但是当我们把int*的数据强制类型转换成char*类型的数据后,再解引用,拿到(访问)的就是一个字节的地址,这时候再进行判断,也就实现我们的目的了。具体代码如下

int main()
{
	int a = 1;
	int* pa = &a;//取出a的地址-int*类型
	char* p = (char*)pa;//强制类型转换成char*类型
	if (*p == 1)//在解引用进行判断
		printf("小端");
	else
		printf("大端");
	return 0;
}

2.3 有符号和无符号的区别

对整型家族来说,有有符号和无符号的区别,拿int 来举例,分为unsigned int(无符号)和signed int(有符号)。

注意:①我们平时直接写的int  short 以及long其实都是有符号的,只是把signed给省略了,但是如果要写无符号,不能省略unsigned

           ② char比较特殊,我们平时写的char到底是signed char还是unsigned char不确定,C语言标准没有给出明确规定,这时就取决于编译器的实现,通常我们见到的编译器下(如VS)一般来说,char指的都是signed char。

那么具体的区别是什么呢?

就拿char举例,char是一个字节,也就是八byte,当是signed char的时候,是有符号数,存在符号位的,也就把最高位当作符号位,符号位为‘0’时,就是正数,符号位为‘1’时就是负数。当是unsigned char的时候,是无符号数,也就没有符号位,把八个byte全部当作数值位来看待。同理int short long都是一样的。

讲到这里,有些同学会有一些疑问,一个有符号char的表示范围是多少呢?一个无符号char的表示范围又是多少呢?

  如上图所示,当我们把八个比特位的所有情况全部写出来后,我们就可以以此计算。

注意:①最高位是符号位 ②10000000表示的是-128

 同理可以算出unsigned char的表示范围。由此可见signed  char的表示范围位-128——127,signed char 表示的范围为0——255。

三、浮点型在内存中的存储

常见的浮点数:3.14159

                         1E10(1.0×10^10)

浮点数家族包括:float  double  long double

浮点数也是有限制范围的,在float.h这个头文件中

我们先来看一串代码的运行结果吧

由上图我们可以得出浮点数在内存的存储以及取出的形式与整型是不同的,是有差异的。那具体是怎么存储的呢?

浮点数的存储规则

根据国际标准 IEEE (电气和电子工程协会) 754 ,任意一个二进制浮点数 V 可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^S表示符号位,当 S=0 V 为正数;当 S=1 V 为负数。
M表示有效数字,大于等于1 ,小于 2
2^E表示指数位。
举例来说:
十进制的 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
IEEE 754 规定:
对于 32 位的浮点数,最高的 1 位是符号位 S ,接着的 8 位是指数 E ,剩下的 23 位为有效数字 M

 

对于 64 位的浮点数,最高的 1 位是符号位S,接着的 11 位是指数 E ,剩下的 52 位为有效数字 M
IEEE 754 对有效数字 M 和指数 E ,还有一些特别规定。
前面说过, 1≤M<2 ,也就是说, M 可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。
IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存后面的 xxxxxx部分。
比如保存 1.01 的时候,只保存01 ,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位浮点数为例,留给M 只有 23 位将第一位的1 舍去以后,等于可以保存 24 位有效数字。

 

至于指数 E ,情况就比较复杂。
首先, E 为一个无符号整数( unsigned int
        这意味着,如果E 8 位,它的取值范围为 0~255 ;如果 E 11 位,它的取值范围为 0~2047 。但是,我们 知道,科学计数法中的E 是可以出 现负数的,所以IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数,对于 8 位的 E ,这个中间数 是127 ;对于 11 位的 E ,这个中间 数是1023 。比如, 2^10 E 10 ,所以保存成 32 位浮点数时,必须保存成 10+127=137 ,即10001001。
然后,指数 E 从内存中取出还可以再分成三种情况:
E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
比如:
0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为
1.0*2^(-1),其阶码为-1+127=126,表示为 01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,
则其二进 制表示形式为: 0 01111110 00000000000000000000000
E全为0
这时,浮点数的指数 E 等于 1-127 (或者 1-1023 )即为真实值,
有效数字 M 不再加上第一位的 1 ,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ±0 ,以及接近于0的很小的数字。
E全为1
这时,如果有效数字 M 全为 0 ,表示 ± 无穷大(正负取决于符号位 s );
好了,关于浮点数的表示规则,就说到这里就结束了 这时再去看看上面的题目的时候就迎刃而解了。
        希望今天这篇文章对你起到一定的帮助。如果觉得小编写的还可以的,可以一键三连(点赞,关注,收藏)哦,你们的支持是对小编极大的鼓励。谢谢。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/781615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全秋招面试题+(含答案)

自我介绍 有没有挖过src&#xff1f; 平时web渗透怎么学的&#xff0c;有实战吗&#xff1f;有过成功发现漏洞的经历吗&#xff1f; 做web渗透时接触过哪些工具 xxe漏洞是什么&#xff1f;ssrf是什么&#xff1f; 打ctf的时候负责什么方向的题 为什么要搞信息安全&#xff0c;对…

人工智能在教育上的应用1-基于pytorch框架下模型训练,用于数学题目图形的智能分类

大家好&#xff0c;今天给大家介绍一下人工智能在教育上的应用1-基于pytorch框架下模型训练&#xff0c;用于数学题目图形的智能分类&#xff0c;本文将利用CNN算法对数学题目中的图形进行自动分类和识别。这种应用可以帮助学生更好地理解和解决与数学相关的问题。基于CNN的数学…

C++平衡搜索二叉树(AVL)

一、定义 AVL树本质上还是一棵二叉搜索树&#xff0c;它的特点是&#xff1a; 1.本身首先是一棵二叉搜索树。 2.带有平衡条件&#xff1a;每个结点的左右子树的高度之差的绝对值&#xff08;平衡因子&#xff09;最多为1。 搜索二叉树可能出现单边树的情况&#xff0c;导致…

JavaWeb 速通HTTP

目录 一、HTTP快速入门 1.HTTP简介 : 2.HTTP请求头 : 3.HTTP响应头 : 二、HTTP响应状态码 1.基本介绍 : 2.常见状态码 : 3.状态码的分类 : 4.完整状态码汇总 : 三、HTTP请求包和响应包 1.请求包分析 : 1 GET请求 (1) 说明 (2) doGet返回数据给浏览器 (3) form表单提…

网络安全合规与标准的主要发展方向

网络安全合规就是避免违反网络安全有关的法律、法规、规章、合同义务以及任何安全要求&#xff0c;标准在网络安全合规工作中扮演着重要的角色。 一、标准在网络安全合规体系中的地位作用 网络安全合规体系包括网络安全有关的法律、法规、规章、其他规范性文件、及合同义务等…

JavaWeb课程设计项目实战(07)——项目编码实践4

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 在本节教程中&#xff0c;我们实现删除学生的功能。当在学生列表中点击删除后即可删除某位学生并重新加载学生列表。所以&#xff0c;该操作实际包含两步&#xff1a; 1、删…

Java虚拟机——虚拟机字节码执行引擎 运行时栈帧结构

1 虚拟机字节码执行引擎 执行引擎是Java虚拟机核心的组成部分之一。"虚拟机"是一个相对于"物理机"的概念&#xff0c;这两种机器都有代码执行的能力&#xff0c;物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的。而虚拟机的执行引擎…

unity 调用c++ dll同时将unity的值传给c++

最近因为项目的需要&#xff0c;实现了将C工程生成dll在unity中调用&#xff0c;但同时发现如果能让unity中值实时的传给c则就完美了&#xff0c;而不只是把c计算的结果给unity。 通过验证是可行的。 C 生成dll导出关键部分 // SPDX-License-Identifier: MIT // Copyright (c…

单Bank OTA升级:STM32G071 BootLoader (一)

什么是单Bank升级&#xff1a;将Flash划分为以下3个区域。 BootLoader区&#xff1a;程序进行升级的引导程序&#xff0c;根据Upade_Flag来判断跳转Bank区运行程序或是接收升级数据写入Bank&#xff0c;接收完成后擦写Upade_Flag区&#xff0c;进行跳转Bank区运行程序。 Upad…

无线投屏手机(安卓)屏幕到 Linux(ubuntu 22.04)桌面

1.安装 scrcpy 安装 scrcpy会自动安装 adb. 这个版本的adb功能不是最全的&#xff0c;需要删掉&#xff0c;然后从链接 https://dl.google.com/android/repository/platform-tools-latest-darwin.zip 下载&#xff0c;解压安装即可。 2. 在手机上 打开开发者模式和 USB调试…

Git Merge和Rebase

◆ 前言 Git作为我们日常开发代码的版本管理&#xff0c;开发分支的管理方面起着很大作用&#xff0c;我们开发过程中分支通常有生产、预发、测试、开发这几个分支&#xff0c;我们会根据项目进行的某个阶段&#xff0c;将代码提交到某个版本上&#xff0c;正常流程是先开发 —…

【并发专题】阻塞队列BlockingQueue实战及其原理分析

目录 前置知识队列有界队列、无界队列Queue——队列在JAVA里面的接口 阻塞队列介绍BlockingQueue——阻塞队列在JAVA里面的接口阻塞队列的应用场景JUC包下的阻塞队列 课程内容*一、ArrayBlockingQueue基本介绍应用场景使用示例基本原理数据结构核心源码解析双指针与环形数组 *二…

设计模式:创建型模式

抽象工厂 abstract factory 例子 考虑一个多风格的界面应用&#xff0c;要求可以切换不同风格类型的组件&#xff08;窗口&#xff0c;滚动条&#xff0c;按钮等&#xff09; 风格/组件pm风格motif风格滚动条pmScrollBarmotifScrollBar窗口pmWindowMotifWindow WidgetFact…

Vue中TodoList案例_静态

MyHeader.vue <template><div class"todo-header"><input type"text" placeholder"请输入你的任务名称&#xff0c;按回车键确认"></div> </template><script> export default {name: "MyHeader"…

16. python从入门到精通——Python网络爬虫

目录 什么是爬虫 优点 网络爬虫的常用技术 网络请求&#xff1a;有三个常用网络请求模块 Urllib模块&#xff1a;python原生系统中标准库模块 urllib中的子模块 urllib.parse.urlencode() 常用于进行 URL 的 get 请求参数拼接 Urllib3模块&#xff1a;Urllib模块的升级版…

paramiko模块使用(2)

远程查看服务器资源使用情况 单机实现 import paramiko# 定义远程服务器的连接信息 hostname 192.168.2.198 username root password 123456# 创建SSH客户端对象 client paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy())try:# 连接到…

力扣刷题SQL-197. 上升的温度---分步解题

表&#xff1a; Weather ------------------------ | Column Name | Type | ------------------------ | id | int | | recordDate | date | | temperature | int | ------------------------ id 是这个表的主键 该表包含特定日期的温度信息编…

qemu搭建arm环境以及文件共享

几乎完全参照该文章 使用QEMU搭建ARM64实验环境 - 简书 ubuntu 14.04&#xff0c;linux3.16&#xff0c; busybox-1.31.0 arm-linux-gnueabi-gcc -v linux3.16以及busybox下载安装可参考链接 Ubuntu14.04安装qemu&#xff0c;运行linux-3.16gdb调试_qemu 安装 ubuntu 14_这个我…

项目开启启动命令整合

启动RabbitMQ管理插件 1.启动 RabbitMQ 管理插件。 rabbitmq-plugins enable rabbitmq_management rabbitmq-server # 直接启动&#xff0c;如果关闭窗⼝或需要在该窗⼝使⽤其他命令时应⽤就会停⽌ rabbitmq-server -detached # 后台启动 rabbitmq-server start # 启⽤服务 rab…

【亲测可用】安装Qt提示“无法下载存档 http://download.qt.io/online/qtsdkrepository...“

下载Qt安装程序exe之后&#xff0c;一般直接双击运行然后&#xff0c;注册登录后&#xff0c;到了第三步【安装程序】时&#xff0c;进行远程检索文件总会卡在这里&#xff0c;无法进行到下一步。报错如下&#xff1a; 解决办法&#xff1a; 关闭安装程序&#xff0c;然后&…