C语言进阶【4】---数据在内存中的存储【1】(你不想知道数据是怎样存储的吗?)
本章概述
- 整数在内存中的存储
- 大小端字节序和字节序判断
- 练习1
- 练习2
- 练习3
- 练习4
- 练习5
- 练习6
- 彩蛋时刻!!!
整数在内存中的存储
- 回忆知识:在讲操作符的那章节中,对于整数而言咱们讲过原码,反码和补码。整数分为有符号整数和无符号整数。有符号整数又分为正整数和负整数。对于有符号整数在内存中的存储为:1个符号位,剩下的全是数值位。对于无符号整数在内存中的存储为:全是数值位。对于有符号整数而言:符号位为1,表示这个整数为负数,符号位为0,表示这个整数为正数。对于负整数,它的原码,反码和补码是不同的。对于正整数和无符号整数,它们的原码,反码和补码是一样的。对于整数而言,我们在内存中存的是它们的补码。(只有整数才有原码,反码和补码的概念)
- 为什么要有原码,反码和补码的概念呢? 在计算机中,整数在内存中存的是它的补码。计算机中只有CPU加法处理器,没有CPU减法处理器。举个例子:
// 我们写个数学式子:5-2=3;在数学中(日常生活中)我们都是直接这样写,没啥问题。
// 但在计算机中只有CPU加法处理器,所以我们要这样写:5+(-2)=3。我们要写成加法才能进行运算。
所以当我们把整数以补码的形式存在内存中时,就可以把符号位也参与计算,这样就省了很多硬件设施。我们就以5+(-2)=3
为例子进行展示计算过程:
// 5----原码: 00000000 00000000 00000000 00000101 (正整数的原码,反码和补码是一样的)
// 反码: 00000000 00000000 00000000 00000101
// 补码: 00000000 00000000 00000000 00000101// -2----原码: 10000000 00000000 00000000 00000010
// 反码: 11111111 11111111 11111111 11111101 (原码取反)
// 补码: 11111111 11111111 11111111 11111110 (反码+1)// 5 00000000 00000000 00000000 00000101+(-2) 11111111 11111111 11111111 11111110 = 00000000 00000000 00000000 00000011 (符号位为0,正整数的原码,反码和补码相同)3所以,以补码的形式存在内存中,符号位也就可以参与计算。
给大家提个小小的总结:整数在内存中是以补码的形式存储,而我们要打印的整数是以原码的形式取出的。
大小端字节序和字节序判断
- 大小端字节序:前面,咱们在C语言内存函数那章节中,提到过大小端字节序。咱们提到过,整形数据在内存中,它的数值位和地址位是相反的——地位数据放在低地址处,高位数据放在高地址处。进行结果调试图展示:
我们创建的int i
占有4个字节,我们知道每4个bit位,为1个16进制位,所以我们创建的int i
能够存储的下,每个字节放两个16进制位。但是,为什么整形数据,在内存中的每个字节,存储的数据与我们的数据顺序不一样呢?这就要引出大小端字节序了。 - 为什么会有大小端字节序呢?
先说个结论:当任何数据的空间大小超过1
个字节的时候,就要考虑数据存放的顺序了。举个生活中的例子来理解:月饼大家都吃过吧,我们知道一个月饼礼盒里面装有很多的月饼,一个月饼礼盒就相当于一块内存,里面的每个月饼就相当于一个个数据。我们知道,里面的月饼使用小盒包装的,这些小盒包装就相当于1个字节。当我们把很多的小盒月饼放进这个礼盒的时候,就要考虑存放的顺序了,比如,先放五仁馅,还是先放草莓馅。这就要考虑放的先后顺序了。这也就是为什么当数据超过1个字节的时候,要考虑存放的顺序了。如图所示:
只要我们能顺利且正确的拿出我们想要的数据,理论上我们可以随便存放数据(只要我们把月饼有秩序的摆好在礼盒中,我们想吃什么口味的月饼,就可以直接拿)。比如,我们存放int i=0x11223344,我们可以分为三大类进行存储,如图所示:
只要你能正确的取出想要的数据,你可以随意排序。但是,为了使C语言更有普及性,就规定了标准,对于整形数据的存储要不采用大端字节序,要不采用小端字节序。具体使用那种方法由编译器决定。我们常见的是小端字节序。 - 大小端字节序的概念:
- 大端字节序:在数据中,从左向右,左端是高位数据,右端是低位数据。高位数据存放在低地址处,低位数据存放在高地址处。(提要:在地址中,左端为低地址,右端为高地址,所取的这个数据地址为这个数据的低地址)如图所示;
- 小端字节序:高位数据放在高地址处,低位数据放在 低地址处。如图所示:
- 大端字节序:在数据中,从左向右,左端是高位数据,右端是低位数据。高位数据存放在低地址处,低位数据存放在高地址处。(提要:在地址中,左端为低地址,右端为高地址,所取的这个数据地址为这个数据的低地址)如图所示;
练习1
请简述⼤端字节序和⼩端字节序的概念,设计⼀个⼩程序来判断当前机器的字节序。(10分)-百度笔试题。
// 思路讲解:我们知道大小端字节序是按单个字节来计算的(排序的),所以我们可以创建一个简单的数据
,对其访问一个字节,就OK。比如,int i =1; 1的大端字节序为 00 00 00 01。1的小端字节序为 01 00 00 00。
当我们访问1的一个字节时,如果访问的数据是0,它就是大端字节序,访问的数据是1,它就是小端字节序。
进行代码展示:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int check()
{int i = 1;return *(char*)&i;
}
int main()
{/*int i = 0x11223344;*/if (check() == 0)printf("大端字节序");elseprintf("小端字节序");return 0;
}
结果运行图:
还有第二种代码,这个代码要用到联合体,进行代码展示:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>union un
{char i;int x;
}s;
int main()
{s.x = 1;if (s.i== 0)printf("大端字节序");elseprintf("小端字节序");return 0;
}
结果运行图:
联合体这个知识点,咱们后面会讲解的,大家现在先了解一下。
练习2
大家可以先猜一下这个代码的运行结果。
#define _CRT_SECURE_NO_WARNINGS 1
#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;}
结果运行图:
不知道大家猜出结果没?咱们接下来进行讲解。
// -1 10000000 00000000 00000000 00000001 原码11111111 11111111 11111111 11111110 反码11111111 11111111 11111111 11111111 补码
// 由于char 只占一个字节,所以-1要发生数据截断,要截取低位数据放到char的空间中
char i =-1 11111111
signed char =char 11111111
unsigned char 11111111%d是打印有符号整数的占位符,char ,signed char ,unsigned char这些数据类型都只有一个字节,不够整形,这个时候就要发生整形提升。
char i =-1 11111111
整形提升 11111111 11111111 11111111 11111111 补码10000000 00000000 00000000 00000001 原码(补码取反+1)打印 -1
signed char =char 和char是一样的过程和结果 打印 -1
unsigned char 00000000 00000000 00000000 11111111 补码00000000 00000000 00000000 11111111 原码(正整数和无符号整数原码,反码和补码相同)打印 255
练习3
大家猜一下运行的结果-----------代码1。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>int main(){char a = -128;printf("%u\n", a);return 0;}
结果运行图:
进行代码解释:
-128 10000000 00000000 00000000 10000000 原码11111111 11111111 11111111 01111111 反码11111111 11111111 11111111 10000000 补码%u打印的是无符号整形
char a 10000000 补码整型提升 11111111 11111111 11111111 10000000 补码(被%u当成无符号整形,全是数值位)11111111 11111111 11111111 10000000 原码打印 4294967168
代码2。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>int main(){char a = 128;printf("%u\n", a);return 0;}
结果运行图:
进行代码解释:
128 00000000 00000000 00000000 10000000 补码
char a 1000000011111111 111111111 11111111 10000000 整形提升11111111 111111111 11111111 10000000 原码打印 4294967168
通过上面的3个代码练习,不知道大家有没有感知到什么重要的信息没有?重要信息——数据类型的意义。它有两个意义:1
.数据类型决定了我们申请空间的大小和访问空间的多大权限。比如,char能申请1个字节,int 能申请4个字节,char* 只能访问一个字节的空间,int*能访问4个字节的空间(这就是权限)2
.数据类型决定了我们如何看待这个数据。比如,-1为整形,但是想放到char里面,就要发生数据截断。%d打印的是有符号整形数据,在%d
眼里面,数据就是无符号整形,在%u
眼里面,数据就是无符号整形。
练习4
大家猜一下运行的结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>int main(){char a[1000];int i;for (i = 0; i < 1000; i++){a[i] = -1 - i;}printf("%d", strlen(a));return 0;}
结果运行图:
进行代码解释:
观察这个代码,我们能够分析出来,它是给数组赋值的代码,我们先不考虑为什么结果与我们的预期不一样。
由分析可知,最终赋值的结果为 -1 ,-2 ,-3,-4……-128,-129,-130……-1000。照咱们这样分析的话,
结果应该是1000,为什么是255呢?
这就要好好来分析一下char
这个数据类型了,我们知道chart是字符类型,用来定义字符的。但是,前面咱们也遇到过char定义整形的,比如,char i=128;咱们讲个重要的知识点,char
类型(包括unsigned char
)是整形的一种特殊形式,它不仅可以定义字符,还可以定义整数。那么接下来,我们来分析一下char
。
char占有1个字节,分为char和unsigned char 进行讨论
char里面的可能存储(-128~127)
1位符号位,7位数值位
0000 0000 --0
0000 0001 --1
0000 0010 --2
0000 0011 --3
0111 1111 --127
…… 整形提升 原码
1000 0000 -- 11111111 11111111 11111111 10000000--10000000 00000000 00000000 10000000 -128
1000 0001 -- 11111111 11111111 11111111 10000001--10000000 00000000 0000000 01111111-127
1000 0010-- -126
……
1111 1111 -- 11111111 11111111 11111111 11111111--10000000 00000000 00000000 00000001 1
我们把char的所有情况已经列出来了,我们发现它最终又回到原来起始的数值,我们可以画个它的数值循环图,如图所示:
当我们的数值超过char的范围的时候,就要重新开始。
unsigned char里面的存储(0~255)
全是数值位
0000 0000 --0
0000 0001 --1
0000 0010 --2
……
1111 1111 --255
unsigned char也符合一个循环图,如图所示:
有了上面的知识铺垫,咱们就可以分析我们上面的代码了,分析如下:
我们知道strlen函数统计的是' \0'前面的数据,遇到' \0'就停止了。我们讲过字符' \0'的AS||值是0,
所以当统计的是字符时,strlen遇到' \0'就停止,统计的是' \0'前面的数据。当统计的是整数时,
遇到0就停止,统计0之前的数据。
char a[1000] -1,-2,-3,……-128,127,126,……0(遇到0停止)。
所以,输出结果:255
大家可以类比一下int和unsigned int ,可以自行画个循环图。
练习5
大家猜一下运行的结果。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{unsigned char i = 0;for (i = 0; i <= 255; i++){printf("hello world\n");}return 0;}
结果运行图:
我们发现,结果直接死循环,为什么会这样呢?接下来进行分析:
unsigned char 的取值范围:0~255 。因为条件是:i<=255.所以当i增加到255时,还是符合条件的,
继续执行程序,只有增加到256时,才会停止程序。前面,由咱们画的循环图可知,
当大于255时就回到起始数值0,以此(0),再次参与计算,因此就会死循环。
我们来个举一反三,看如下的代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{unsigned int i;for (i = 9; i >= 0; i--){printf("%u\n", i);}return 0;
}
结果运行图:
可以看出来,结果也是死循环。这个结果的原因和刚才代码的原因是相同的,大家可自行分析一下。
练习6
大家猜一下运行的结果(注意:在X86条件下运行代码)。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//X86环境 ⼩端字节序
int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);printf("%x,%x", ptr1[-1], *ptr2);return 0;
}
结果运行图:
进行代码讲解:&a
取出的是整个数组的地址,&a+1
跳过整个数组(这些都是前面的知识点,忘记的同学自行回顾去!!!)。如图所示:
我们先来分析一下这两个代码:int* ptr1 = (int*)(&a + 1)
;和ptr1[-1]
。
int* ptr1 = (int*)(&a + 1)
;这个代码中,(&a+1)
取到的是4后面的空间地址。然后,把数组指针转为int*
指针——(int *)(&a+1)
,赋值于ptr1
。到目前为止,ptr1[-1]
这种数组的访问方式还是第一次见,[-1]
的意思就是,在原来的位置上往前访问1个空间,[-2]
就是在原来的位置上往前访问2个空间。如图所示:
所以,ptr1[-1]访问的结果是4.
我们再来解释一下这两个代码:int * ptr2 = (int *)((int)a + 1);和 *ptr2。数组名就是地址(首元素的地址),(int)a就是把地址转换成了整形。假设,a的地址就是 0x11223344,(int)之后这个地址编号就变成了整形数据了,它不再是地址了。所以,(int)a+1就是个普通的整形计算,0x11223344+1=0x11223345
。然后。把这个值赋值再转换位整型指针—— (int *)((int)a + 1)赋值给ptr2。所以,当我们 *ptr2访问的就是个很大的整数——2000000。
彩蛋时刻!!!
https://www.bilibili.com/video/BV1Jz4y1L7AR/?spm_id_from=333.337.search-card.all.click&vd_source=7d0d6d43e38f977d947fffdf92c1dfad
每章一句:心中有火,眼里有光。
感谢你能看到这里,点赞+关注+收藏+转发是对我最大的鼓励,咱们下期见!!!
相关文章:

C语言进阶【4】---数据在内存中的存储【1】(你不想知道数据是怎样存储的吗?)
本章概述 整数在内存中的存储大小端字节序和字节序判断练习1练习2练习3练习4练习5练习6 彩蛋时刻!!! 整数在内存中的存储 回忆知识:在讲操作符的那章节中,对于整数而言咱们讲过原码,反码和补码。整数分为有…...

【mysql面试题】mysql复习之常见面试题(一)
本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》:python零基础入门学习 《python运维脚本》: python运维脚本实践 《shell》:shell学习 《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战 《k8…...

VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。以下是在VB.NET中…...

vue2+js项目升级vue3项目流程
Vue 3 相较于 Vue 2 在性能、特性和开发体验上都有了显著的提升。升级到 Vue 3 可以让你的项目受益于这些改进。但是,升级过程也需要谨慎,因为涉及到代码的重构和潜在的兼容性问题。 1. 升级前的准备 备份项目: 在开始升级之前,…...

做EDM邮件群发营销时如何跟进外贸客户?
跟进外贸客户是外贸业务中至关重要的一环,需要耐心和策略。以下是一些建议,帮助你有效跟进外贸客户: 充分了解产品: 深入了解自己的产品,包括品质、价格竞争力、适用市场等。 只有对产品有充分的了解,才…...

【Java经典游戏】-01-是男人就坚持30秒
hello!各位彦祖们!我们又见面了!! 今天兄弟我给大家带来了一款经典趣味小游戏的项目案例-是男人就坚持30秒 本项目案例涉及到的技术: Java 语法基础Java 面向对象JavaSwing 编程Java 线程 是一个非常适合小白来加强…...

微调框QSpinBox
作用:允许用户按照一定的步长,来增加或减少其中显示的数值 有两种类型的微调框 QSpinBox - 用于整数的显示和输入QDoubleSpinBox - 用于浮点数的显示和输入 值 包括最大值、最小值、当前值 // 获取和设置当前值 int value() const void setValue(in…...

在线查看 Android 系统源代码 AOSPXRef and AndroidXRef
在线查看 Android 系统源代码 AOSPXRef and AndroidXRef 1. AOSPXRef1.1. http://aospxref.com/android-14.0.0_r2/1.2. build/envsetup.sh 2. AndroidXRef2.1. http://androidxref.com/9.0.0_r3/2.2. build/envsetup.sh 3. HELLO AndroidReferences 1. AOSPXRef http://aospx…...

JavaScript substr() 方法
定义和用法 substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。 <script type"text/javascript">var str"Hello world!" document.write(str.substr(3))</script>lo world!<script type"text/javascript">v…...

教你把图片转换为炫酷的翻页电子杂志
翻页电子杂志以其炫酷的视觉效果和便捷的阅读方式,受到了许多用户的喜爱。想要把普通的图片转换成这样的效果,其实并不复杂。下面,就让我来为您介绍一下如何操作。 首先,您需要准备一些基本的工具和材料。您需要一个图像编辑软件…...

生信软件35 - AI代码编辑器Cursor
1. Cursor - AI代码编辑器 Cursor的核心功能是利用生成式AI,帮助程序员通过自然语言描述快速生成代码。让程序员未来需要关注的是“做什么”(What)而不是“怎么做”(How),即在使用AI生成代码的基础上&…...

Vue Router 编程式导航全攻略:深入掌握 push, replace, go, back, forward,beforeEach 方法
Vue Router 编程式导航全攻略:深入掌握 push, replace, go, back, forward,beforeEach 方法 在Vue Router中,编程式导航是一种通过JavaScript代码来实现路由跳转的方法。与声明式导航(使用<router-link>标签)相比ÿ…...

切换淘宝最新镜像源:优化NPM包管理的极致体验
在NPM生态系统中,快速、安全地获取所需的包是每个前端工程师追求的目标。然而,由于不同地区的网络环境,直接通过官方NPM仓库获取包可能会导致下载速度缓慢、超时等问题。针对这些情况,淘宝团队提供了优秀的NPM镜像源,并且定期更新。本文将详尽介绍如何切换淘宝最新镜像源,…...

react 基础语法
前置知识 类的回顾 通过class关键字定义一个类 类名首字母大写 class类有constructor构造器 new 一个类得到一个实例 类还有方法,该方法也会在其原型上 static静态数据,访问静态属性通过 类名.id getter和setter getter:定义一个属性&…...

k8s的NodeIP、PodIP、ClusterIP、ExternalIP
1.NodeIP K8s集群由Master Node与Worker Node组成。 Node:组成k8s集群的机器,可以是物理机或虚拟机。 Master Node :管理节点也叫控制平面主要负责管理控制方面。 Worker Node::工作节点用于部署处理业务的工作负载或p…...

【vue element-ui】关于删除按钮的提示框,可一键复制
实现效果: Delete: function (id) {this.$confirm(此操作将永久删除该文件, 是否继续?, 提示, {confirmButtonText: 确定,cancelButtonText: 取消,type: warning,center: true,}).then(() > {Delete(id).then(() > {this.$message({type: success,message: 删…...

内部工具使用
1. displaytool 开发的渲染工具,如将车端建图结果显示在渲染窗口中,便于查bug 2. localization / csmap 开发的定位工具 和 车端建图工具 3. bolepack 第三方,处理感知数据的工具 运行流程:1-> 2 -> 3 bol…...

Spring Boot-静态资源管理问题
在Spring Boot中,静态资源管理是构建现代Web应用程序时必不可少的一部分。无论是处理静态页面、图片、CSS、JavaScript文件,还是一些自定义文件,正确管理这些资源能够提升用户体验和优化应用的性能。 1. Spring Boot中的静态资源管理概述 S…...

白酒与商务宴请:如何成为餐桌上的受宠者之一?
在商务宴请的场合中,白酒往往是餐桌上不可或缺的佳酿。一瓶好的白酒,不仅能够彰显主人的品味,还能为宾客带来愉悦的享受。那么,在商务宴请中,如何选择一瓶合适的白酒,让自己成为餐桌上的受宠者之一呢&#…...

【C语言零基础入门篇 - 9】:文件操作
文章目录 文件操作文件的简介指向指针的文件文件的打开方式字符的读取和存储数据的读取和存储 文件操作 文件的简介 一、什么是文件? 文件有不同的类型,主要有两种文件: (1)程序文件。(2)数据…...

链式二叉树的基本操作(C语言版)
目录 1.二叉树的定义 2.创建二叉树 3.递归遍历二叉树 1)前序遍历 2)中序遍历 3)后序遍历 4.层序遍历 5.计算节点个数 6.计算叶子节点个数 7.计算第K层节点个数 8.计算树的最大深度 9.查找值为x的节点 10.二叉树的销毁 从二叉树…...

Tcp三次握手四次挥手和SSL/TLS
1.Tcp三次握手四次挥手: 1.1基本概念: TCP(三次握手和四次挥手)是用于建立和终止可靠传输连接的过程。TCP协议是一种面向连接的传输层协议,确保数据在网络上可靠、有序地传输。下面详细解释三次握手和四次挥手的工作机…...

大棚分割数据集,40765对影像,16.9g数据量,0.8米高分二,纯手工标注(arcgis标注)的大规模农业大棚分割数据集。
数据集名称: )“Greenhouse Segmentation Dataset (GSD)” 数据集规模: 包含40,765对用于大棚分割的影像数据,每对影像包括一张原始图像和相应的分割标签图。 数据量: 总数据量约为16.9GB,适合存储在现…...

Jenkins插件安装失败时这么做就搞定啦!
1.网络或墙的问题导致插件下载安装失败 这种错误提示很明显,就是无法连接到插件下载地址,导致插件下载失败。 解决方法 为Jenkins更换源 点击Jenkins主页面左侧列表中【系统管理】—— 下拉找到【管理插件】 选择【高级】选项卡 替换最下方【升级站点…...

优化器与现有网络模型的修改
文章目录 一、优化器是什么二、优化器的使用三、分类模型VGG16四、现有网络模型的修改 一、优化器是什么 优化器(Optimizer)是一个算法,用于在训练过程中调整模型的参数,以便最小化损失函数(Loss Function)…...

kafka 超详细的消息订阅与消息消费几种方式
kafka 消息订阅与消息消费几种方式 本文主要内容 消费者订阅几种方式 订阅多个主题 按正则表达式订阅 消息消费几种方式 按分区消费 按主题消费 不区分 “ 笔者建议一开始学习Kafka最好不要用SpringBoot 集成方式,因为SpringBoot推崇用注解方式,比如KafkaList…...

C++ 第三讲:内存管理
C 第三讲:内存管理 1.C内存分布2.内存管理方式2.1C语言内存管理方式2.2C内存管理方式2.2.1new\delete操作内置类型2.2.2new\delete操作自定义类型 3.operator new与operator delete函数4.new和delete实现原理4.1内置类型4.2自定义类型 5.定位new5.1内存池的基本了解…...

LeeCode打卡第二十九天
LeeCode打卡第二十九天 第一题:岛屿数量(LeeCode第200题): 给你一个由 1(陆地)和 0(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只…...

阿里云专业翻译api对接
最近我们一个商城项目涉及多语言切换,默认中文。用户切换语言可选英语和阿拉伯语言,前端APP和后端返回动态数据都要根据用户选择语言来展示。前端静态内容都做了三套语言,后端商品为了适用这种多语言我们也进行了改造。每一件商品名称&#x…...

基于Spring Boot的能源管理系统+建筑能耗+建筑能耗监测系统+节能监测系统+能耗监测+建筑能耗监测
介绍 建筑节能监测系统是基于计算机网络、物联网、大数据和数据可视化等多种技术融合形成的一套节能监测系统。 系统实现了对建筑电、水、热,气等能源、资源消耗情况的实时监测和预警、动态分析和评估,为用户建立了科学、系统的节能分析方法,…...