首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >编辑器对内存的使用——数据的保存与访问使用(整形篇)

编辑器对内存的使用——数据的保存与访问使用(整形篇)

作者头像
比特大冒险
发布2023-04-16 16:56:07
发布2023-04-16 16:56:07
7260
举报
文章被收录于专栏:小白历险记小白历险记


🏆前言

当你不断的在你五彩斑斓的编辑器上敲一串又一串的代码时,你会不会思考这些代码是如何实现的呢?有人会说有打包好封装好的库函数给我们使用,但是这些函数又是靠的什么来实现的呢?而且代码也不只有函数,还有各种的操作符,它们又是如何实现的呢?

       当你由于打错某个类型的名称时,编辑器总是给出一个错误但是唯一或随机的结果,你又是如何理解的呢?

        这篇(系列)文章可能会解开你的这些疑问。

由于篇幅有限这篇我们仅讨论数据的存储与访问


提示:以下是本篇文章正文内容,下面案例可供参考

一、🤔数据类型的意义,如何实现的?

面对需求,我们会根据不同的情况去设置变量来实现不同的功能,但是编辑器如何实现的呢?

🚗我们都知道计算机都是2进制的,所以所有的数据都是以2进制的方式来存入电脑内存的

🚕此时新的疑问又来了,都是以2进制的方式存入的,那电脑是如何分辨不同的数据类型的存储和访问呢?

🚓编辑器这里采用了类似解密码的原理,首先这里的不同数据的类型对应不同的加密和解密方式,使用相应的类型(每种类型都可以看作成单独的一套解密和加密)密钥将数据加密为一串二进制数在存入内存中,当访问时在用相应的密钥解开即可,这样便做到了用不同的类型密钥来分辨电脑中都是二进制码的分类储存

🚌此时我们就能理解为什么在给变量定义时要写变量类型(告诉编辑器要使用的对应密钥),也能够理解为什么当我们用不同的类型去定义和访问同一个变量时,有时编辑器不但不会报错还会得到一个奇怪的结果(访问解密时的对象都是2进制数所以可以解但是解出来不一定会正确的值有些类型解密的方式有相似之处)

基本的内置类型

二、👻常见的数据类型

🤔1.分类

我们将常见的内置数据类型分为2大类整形家族浮点数家族,至于为什么,这可以和上文的“密钥”有一定的关系

🚄整形家族中它们的“密钥”的主要区别在于signed(有符号)、unsigned(无符号)和定义“加密2进制”和“解密2进制”时使用的内存大小。

🚅浮点数家族中则完全不同(详细请阅读后续文章)

构造类型(自定义类):

代码语言:javascript
复制
> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union

指针类型:

代码语言:javascript
复制
int *pi;
char *pc;
float* pf;
void* pv;

空类型:

 void 表示空类型(无类型) 通常应用于函数的返回类型、函数的参数、指针类型。

👀2.整形家族

整形家族中的成员与对应范围

 ⛵(1)存入整形家族的数据(加密)

①unsigned(无符号):首先根据定义的类型开辟对应内存大小用于存储,然后直接将数化为2进制存入开辟的空间

②signed(有符号):首先根据定义的类型开辟对应内存大小用于存储,然后将数化为2进制得到源码,再依次转化为反码——补码,最后将补码存入内存

反码:为负号时,将源码按位取反。

           为正号时,不变.

补码:为负号时,将反码加“1”

为正号时,不变.

💀特别的:如果得到的二进制值大于,所用的数据类型,此时会发生“截断”,即会丢失多出存储的范围(这也是精度丢失的主要原因)

🚢(2)访问与使用时整形家族的数据(解密)

首先整形提升(有些类型不需要整形提升)如果有必要还会有算数转化,再根据所给的类型去相应解密(可以和定义时给的不同,但结果可能千奇百怪😂)

🚤(3)对于整形来说:数据存放内存中其实存放的是补码。

为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统 一处理; 同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的(补码也可以加一然后按位取反得到源码),不需要额外的硬件电路。

我们看看在内存中的存储:

我们可以看到对于a和b分别存储的是补码。但是我们发现顺序有点不对劲。 这是又为什么? 

 什么是大端小端:

现实中我们的书写方式

如上图我们可以发现,我们的书写方向对于这个数来说是从高位到低位,但在计算机则还有一个方向——存储方向(低位到高位),此时书写方向(存储方向)与数字的高低位方向是是相反的,此时我们称为:小端模式

大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址 中。(我们现实生活中都为大端书写)

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

为什么有大端和小端:

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

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

代码语言:javascript
复制
//代码1
#include <stdio.h>
int check_sys()
{
 int i = 1;
 return (*(char *)&i);
}
int main()
{
 int ret = check_sys();
 if(ret == 1)
 {
 printf("小端\n");
 }
 else
 {
 printf("大端\n");
 }
 return 0;
}
//代码2
int check_sys()
{
 union
 {
 int i;
 char c;
 }un;
 un.i = 1;
 return un.c;
}

🚢(4)整形提升

C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

整型提升的意义: 表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。(节约资源)

通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

代码语言:javascript
复制
//实例1
char a,b,c;
...
a = b + c;

b和c的值被提升为普通整型,然后再执行加法运算。加法运算完成之后,结果将被截断,然后再存储于a中。

如何进行整体提升呢?

整形提升是按照变量的数据类型的符号位来提升的

代码语言:javascript
复制
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

整形提升的例子: 

代码语言:javascript
复制
//实例1
int main()
{
 char a = 0xb6;
 short b = 0xb600;
 int c = 0xb6000000;
 if(a==0xb6)
 printf("a");
 if(b==0xb600)
 printf("b");
 if(c==0xb6000000)
 printf("c");
 return 0;
}

实例1:中的a,b要进行整形提升,但是c不需要整形提升a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真. 所程序输出的结果是: c 

代码语言:javascript
复制
//实例2
int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));
 printf("%u\n", sizeof(+c));
 printf("%u\n", sizeof(-c));
 return 0;
}

实例2:中的,c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字 节. 表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节.

🛬(5)算术转化

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算。

警告: 但是算术转换要合理,要不然会有一些潜在的问题。 

代码语言:javascript
复制
float f = 3.14;
int num = f;//隐式转换,会有精度丢失

总结

该文章目前只简绍了整形家族,浮点数家族还未简绍(这才是难点),在全部写完后还会写一些相关的神神奇奇的题希望大家支持支持😘

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🏆前言
  • 一、🤔数据类型的意义,如何实现的?
  • 二、👻常见的数据类型
    • 🤔1.分类
    • 👀2.整形家族
      •  ⛵(1)存入整形家族的数据(加密)
      • 🚢(2)访问与使用时整形家族的数据(解密)
      • 🚤(3)对于整形来说:数据存放内存中其实存放的是补码。
      • 🚢(4)整形提升
      • 🛬(5)算术转化
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档