幽微之处见真章:数据类型与内存存储的内在联系

news2024/9/20 22:24:37

嘿嘿,家人们,今天咱们来深度剖析数据类型在内存中的存储,好啦,废话多不讲,开干!


1.:数据类型介绍

在前面呢,博主已经介绍了基本的数据类型:

char                 //字符数据类型                  ---->占据1个字节
short               //短整型                             ---->占据2个字节
int                    //整形                                 ---->占据4个字节
long                //长整型                               ---->占据4个字节
long long        //更长的整形                        ---->占据8个字节
float                //单精度浮点数                    ---->占据4个字节
double            //双精度浮点数                    ---->占据8个字节
那么这些基本的数据类型,这些类型的意义在哪呢?
  1. 使用这个类型开辟内存空间大小(大小则决定了使用的数值范围).
  2. 为学习者提供了一个看待内存空间的视角.

1.1:类型的归类

1.1.1:整型家族

char

        unsigned char

        signed     char

PS:字符型的数据在内存中是以ascii码值的方式存储的,因此属于整型家族.

 

short

        unsigned short int(无符号短整型)

        signed     short int(有符号短整型)

 

int 

        unsigned int(无符号短整型)

        signed     int(有符号短整型)

 

long

        unsigned lont int(无符号长整型)

        signed            int(有符号长整型)

1.1.2:浮点数家族

float            浮点型

double         双精度浮点型

1.1.3:构造类型

数组类型

结构体类型   struct

枚举类型       enum

联合类型       union

1.1.4:空类型

void 表示空类型(无类型).

通常应用于函数的返回类型、函数的参数、指针类型.

2:整数在内存中的存储

在操作符的时候,博主有讲解到原码,反码,补码的相关知识,这里带着uu们简单回顾下.

  1. 整数的2进制表⽰⽅法有三种,即 原码、反码和补码
    三种表⽰⽅法均有 符号位和数值位 两部分,符号位都是⽤ 0表⽰“正” ⽤1表⽰“负 ”, 最⾼位的⼀位是被当做符号位 ,剩余的都是数值位.
  2. 正整数的原、反、补码都相同.
  3. 负整数的三种表⽰⽅法各不相同。
    (1): 原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
    (2): 反码: 符号位不变,其他位 依次按位取反就可以得到反码。
    (3): 补码:反码+1就得到补码。
对于整形来说: 数据存放在内存中其实存放的是补码.这是为什么呢
(1):在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于: 使⽤补码,可以将符号位和数值域统⼀处理.
(2): 同时,加法和减法也可以统⼀处理( CPU只有加法器 )此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路.
这里我们举个几个简单的例子

3:大小端字节序和字节序判断

当我们了解了整数在内存中的存储方式后,接下来我们来通过调试看一个小细节.

通过调试,我们能够清晰地看到,在a中的 0x12345678 这个数字是按照字节为单位,倒着存储的。这是为什么呢?这里就要涉及到大小端存储啦

3.1什么是大小端

超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,将其分为 ⼤端字节序存储和⼩端字节序存储 .
  • 大端存储模式:是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处.
  • 小端存储模式:是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的低地址处.

3.2:为什么会有大小端模式之分呢

  1. 在计算机系统中,是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit.但是在C语言中除了8bit的char类型数据外,还有16bit的short,32bit的int,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题.因此就导致了大端存储模式与小端存储模式.
  2. 一个 16bit short x , 在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11
    高字节, 0x22 为低字节。对于 大端模式,就将 0x11 放在低地址 中,即 0x0010 中, 0x22 放在高地址 中,即 0x0011 中。 小端模式,刚好相反 。我们常用的 X86 结构是小端模式 ,而 KEIL C51 则为大端模式。很多的ARM DSP 都为小端模式。有些 ARM 处理器还可以由硬件来选择是大端模式还是小端模式。

3.3:判断一个机器的字节序

当了解了大小端的概念后,那么我们该如何判断一个机器是大端存储方式还是小端存储方式.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int check_sys()
{
	int value = 1;
	return (*(char*)&value & 1);
}
int main()
{
	int result = check_sys();
	if(result == 0)
	{
		printf("大端存储\n");
	}
	else
	{
		printf("小端存储\n");
	}

}

4:相关练习

了解了整数在内存中的存储后,接下来我们来看几个小练习

4.1:练习1

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

4.2:练习2

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

4.3:练习3

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

4.4:练习4

#include <stdio.h>
#include <string.h>
int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

4.5:练习5

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

上述代码呢,很明显,会发生死循环,那么是为什么呢?

4.6:练习6

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

通过观察上面的结果,我们可以看到,此代码也会发生死循环,那么是为什么呢,有的uu会很奇怪,当i ==0时,此时再对其进行--,不应该是到 -1了吗,那么这是为啥呢?

5:浮点数在内存中的存储

常见的浮点数:3.14159、1E10等,浮点数家族包括:floatdoublelong double 类型.那么浮点数在内存中是如何存储的呢,我们首先来看下面这段代码.

5.1:代码1

#include <stdio.h>
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明明在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大,这是为什么呢?这里就涉及到了浮点数在内存中的存储方式了.

5.2:浮点数存储规则

根据国际标准IEE754标准,任何一个二进制浮点数V可以表示成下面的形式.

  • V = (-1)^S * M * 2 ^ E
  • (-1)^S表示符号位,当S为0时,V为整数,当S为1时,V为负数.
  • M表示有效数字,M是大于等于1,小于2的.
  • 2^E表示指数位.

了解了浮点数的存储规则后,我们来看几个例子.

5.2.1:例子1

5.2.2:例子2

5.2.3:IEE754规定

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

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

5.3:浮点数存储的过程

IEE754对有效数字M和指数M,有一些特别的规定.

在上面有提到过,1 <= M < 2,那么也就是说,M可以写成1.xxxxxxx的形式,其中xxxxxxx表示小数部分.

IEE754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分.比如保存1.01的时候,只保存01,等到要对其进行读取的时候,再把第一位的加上去.这样子做的目的是为了节省一位有效数字.以64位浮点数为例,留给M的只有52位,将第一位的1加进来以后,等于可以保存53位有效数字.

至于指数E,情况就有些复杂.

首先,E为一个无符号整数(unsigned int)

这就意味着,若E为8位,那么它的取值范围为0~255;如果E为11位,它的取值范围为0~2047.但是,我们知道,科学计数法中的E是可以出现负数的,所以IEE754规定,存入内存E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023.例如,2^10的E是10,所以保存为32位浮点数时,必须保存成10 + 127 = 137.即10001001.

5.4:浮点数取的过程

指数E从内存中取出还可以再分成三种情况

5.4.1:E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(32位浮点数)或1023(64位浮点数),得到真实值,再将有效数字M前加上第一位的1.

比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1 + 127 = 126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位,那么二级制表示形式则为

0 01111110 00000000000000000000000.

5.4.2:E为全0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的⼩数。这样做是为了表示±0,以及接近于0的很小的数字。

5.4.3:E为全1

这个时候,如果有效数字M全为0,表示±无穷大.

6:题目解析

了解了浮点数的存储过程后,我们再回到最初的代码

#include <stdio.h>
int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
    9
    /*
    补码:000000000000000000000000000001001

    从浮点数存储的角度出发
    
    0(S) 00000000(E) 000000000000000000001001(M)
    
    由于E为全0,那么此时E的真实值是1 - 127(32位) = -126

    M = 0.000000000000000000001001

    S = 0 

    因此V = (-1)^0 * 0.000000000000000000001001 * 2^-126;*/
	printf("*pFloat的值为:%f\n", *pFloat);//无限接近于0
	*pFloat = 9.0;
    /*二进制序列:1001.0

    V = (-1)^0 * 1.001 * 2^3;

    S = 0, M = 1.001 , E = 3;

    将其还原为二进制序列时,

    首先E + 127(32位) = 130------->10000010
    M去掉整数位后为001,然后后面补0,
    因此二进制序列是

    0(S) 10000010 (3 + 127---->E)001 0000 0000 0000 0000 0000*/
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

 

9

补码:000000000000000000000000000001001

从浮点数存储的角度出发

0(S) 00000000(E) 000000000000000000001001(M)

由于E为全0,那么此时E的真实值是1 - 127(32位) = -126

M = 0.000000000000000000001001

S = 0 

因此V = (-1)^0 * 0.000000000000000000001001 * 2^-126-------->无线接近于0

 

9.0

二进制序列:1001.0

V = (-1)^0 * 1.001 * 2^3;

S = 0, M = 1.001 , E = 3;

将其还原为二进制序列时,

  • 首先E + 127(32位) = 130------->10000010
  • M去掉整数位后为001,然后后面补0,

因此二进制序列是

0(S) 10000010 (3 + 127---->E)001 0000 0000 0000 0000 0000

好啦,uu们,数据类型在内存中的存储这部分滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~

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

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

相关文章

51单片机STC89C52RC——18.1 HC-SR04超声波测距

目的/效果 独立按键K1按下后开始测距&#xff0c;LCD显示距离&#xff08;mm&#xff09; 一&#xff0c;STC单片机模块 二&#xff0c;HC-SR04 超声波测距 2.1 HC-SR04 简介 HC-SR04超声波测距模块提供2cm~400cm的测距功能&#xff0c;精度达3mm。 2.2 时序 以上时序图表明…

前端面试题(JS篇五)

一、同步与异步的区别 同步指的是当一个进程在执行某一个请求的时候&#xff0c;如果这个请求需要等待一段时间才能返回&#xff0c;那么这个进程会一直等待下去&#xff0c;直到这个消息返回之后才会继续执行。 指的是当一个进程在执行某一个请求的时候&#xff0c;如果这个请…

Leetcode 1302.层数最深子叶结点的和

大家好&#xff0c;今天我给大家分享一下我关于这个题的想法&#xff0c;我这个题过程比较复杂&#xff0c;但大家如果觉得好的话&#xff0c;就请给个免费的赞吧&#xff0c;谢谢了^ _ ^ 1.题目要求: 给你一棵二叉树的根节点 root &#xff0c;请你返回 层数最深的叶子节点的…

初学者如何通过建立个人博客盈利

建立个人博客不仅能让你在网上表达自己&#xff0c;还能与他人建立联系。通过博客&#xff0c;可以创建自己的空间&#xff0c;分享想法和故事&#xff0c;并与有相似兴趣和经历的人交流。 本文将向你展示如何通过建立个人博客来实现盈利。你将学习如何选择博客主题、挑选合适…

【华为OD笔试】2024D卷命题规律解读【分析300+场OD笔试考点总结】

可上 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1441了解算法冲刺训练&#xff08;备注【CSDN】否则不通过&#xff09; 文章目录 相关推荐阅读华为OD笔试2024D卷命题规律解读华为OD算法/大厂面试高频题算法练习冲刺训练 相关推荐阅读 【华为OD笔试】2024D卷机考套题…

Android Framework学习笔记(4)----Zygote进程

Zygote的启动流程 Init进程启动后&#xff0c;会加载并执行init.rc文件。该.rc文件中&#xff0c;就包含启动Zygote进程的Action。详见“RC文件解析”章节。 根据Zygote对应的RC文件&#xff0c;可知Zygote进程是由/system/bin/app_process程序来创建的。 app_process大致处…

LLM 的储备知识

GPT一代 模型堆叠了12个解码器层。由于在这种设置中没有编码器&#xff0c;这些解码器层将不会有普通transformer解码器层所具有的编码器-解码器注意力子层。但是&#xff0c;它仍具有自注意力层。 训练过程 Transformer Decoder 结构 编码器&#xff08;6 layers&#xff09…

Template_C++

C模板 C提供了function template. function template&#xff1a;实际上是建立一个通用函数&#xff0c;其函数类型和形参类型不具体制定&#xff0c;用一个虚拟的类型来代表。这个通用的函数就称为函数模版。 是不是可以这样理解&#xff0c;函数模版就是给了一种功能&…

Linux 下 ElasticSearch 集群部署

目录 1. ElasticSearch下载 2. 环境准备 3. ElasticSearch部署 3.1 修改系统配置 3.2 开放端口 3.3 安装 ElasticSearch 4. 验证 本文将以三台服务器为例&#xff0c;介绍在 linux 系统下ElasticSearch的部署方式。 1. ElasticSearch下载 下载地址&#xff1a;Past Rel…

vue 如何做一个动态的 BreadCrumb 组件,el-breadcrumb ElementUI

vue 如何做一个动态的 BreadCrumb 组件 el-breadcrumb ElementUI 一、ElementUI 中的 BreadCrumb 定义 elementUI 中的 Breadcrumb 组件是这样定义的 <template><el-breadcrumb separator"/"><el-breadcrumb-item :to"{ path: / }">主…

算法 —— LRU算法

算法 —— LRU算法 LRULRU算法的工作原理&#xff1a;实现方法&#xff1a;性能考虑&#xff1a; 模拟过程splice函数对于std::list和std::forward_list基本语法&#xff1a;功能描述&#xff1a; 示例&#xff1a;注意事项&#xff1a; 如果大家已经学习过了Cache的替换算法和…

《人性的弱点》

This book is called ‘How to Win Friends & Influence People’. [COPY] 卡耐基《人性的弱点》有什么干货么&#xff1f;

自学鸿蒙HarmonyOS的ArkTS语言<十>@BuilderParam装饰器

作用&#xff1a;当子组件多处使用时&#xff0c;给某处的子组件添加特定功能 一、初始化 1、只能被Builder装饰的方法初始化 2、使用所属自定义组件的builder方法初始化 3、使用父组件的builder方法初始化 - 把父组件的builder传过去&#xff0c;参数名和子组件的builderPar…

【信号频率估计】MVDR算法及MATLAB仿真

目录 一、MVDR算法1.1 简介1.2 原理1.3 特点1.3.1 优点1.3.2 缺点 二、算法应用实例2.1 信号的频率估计2.2 MATLAB仿真代码 三、参考文献 一、MVDR算法 1.1 简介 最小方差无失真响应&#xff08;Mininum Variance Distortionless Response&#xff0c;MVDR&#xff09;算法最…

AI初学者的利器——香橙派AIpro

目录 引言香橙派介绍公司简介&#xff08;来自官网&#xff09;香橙派AIpro介绍香橙派AIPro硬件规格参数开发板接口详情系统登陆与使用指示灯 AI运行实例AI CPU和control CPU的设置方法香橙派AIpro cpu知识查询AIcpu占用率与cpu类别设置 Juypter lab使用JuypterLab介绍JuypterL…

8款可以替代Axure的设计软件推荐

一个好的原型设计工具对于产品经理或者UI/UX设计师来说非常重要。一个好的原型设计软件可以帮助你快速构建一个还原度高、信息结构清晰的原型图&#xff0c;也可以大大降低工作中与同事的沟通成本&#xff0c;更高效地推进工作。 那么&#xff0c;什么是易于使用和免费的原型设…

C51语言及通用I/O口应用

4.1 C51的程序结构 4.2 C51的数据结构 4.3 C51与汇编的混合编程 4.4 C51仿真开发方法 4.5 通用I/O口的简单应用 4.6 通用I/O口的进阶应用 4.1.1 C51语言概述 C51语言是51单片机的一种高级编程语言&#xff0c;与低级语言的汇编语言相比&#xff0c;一方面具有结构化语…

Chapter12 屏幕后处理效果——Shader入门精要学习笔记

Chapter12 屏幕后处理效果 一、屏幕后处理概述以及基本脚本系统1.OnRenderImage 函数 —— 获取屏幕图像2.Graphics.Blit 函数 —— 使用特定的Shader处理3.在Unity中实现屏幕后处理的基本流程4.屏幕后处理基类 二、调整亮度、饱和度和对比度1.BrightnessSaturationAndContrast…

Postman安装使用教程(详解)

目录 一、Postman是什么 二、安装系统要求 三、下载Postman 四、注册和登录Postman 五、创建工作空间 六、创建请求 一、Postman是什么 在安装之前&#xff0c;让我们先来简单了解一下Postman。Postman是一个流行的API开发工具&#xff0c;它提供了友好的用户界面用于发送…

简单实用的企业舆情安全解决方案

前言&#xff1a;企业舆情安全重要吗&#xff1f;其实很重要&#xff0c;尤其面对负面新闻&#xff0c;主动处理和应对&#xff0c;可以掌握主动权&#xff0c;避免股价下跌等&#xff0c;那么如何做使用简单实用的企业舆情解决方案呢&#xff1f; 背景 好了&#xff0c;提取词…