C语言进阶【4】---数据在内存中的存储【1】(你不想知道数据是怎样存储的吗?)

news2024/9/19 21:50:07

本章概述

  • 整数在内存中的存储
  • 大小端字节序和字节序判断
        • 练习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("大端字节序");
	else
		printf("小端字节序");
	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("大端字节序");
	else
		printf("小端字节序");
	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              10000000
         11111111 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在这里插入图片描述
每章一句心中有火,眼里有光。感谢你能看到这里,点赞+关注+收藏+转发是对我最大的鼓励,咱们下期见!!!

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

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

相关文章

JAVA同城生活新引擎外卖跑腿团购到店服务多合一高效系统小程序源码

&#x1f680;同城生活新风尚&#xff01;一站式高效系统&#xff0c;让日常更便捷&#x1f6cd;️ &#x1f37d;️【开篇&#xff1a;同城生活&#xff0c;一触即发】&#x1f37d;️ 在这个快节奏的时代&#xff0c;同城生活的便利性与效率成为了我们追求的新风尚。想象一下…

C++ —— 关于vector

目录 链接 1. vector的定义 2. vector的构造 3. vector 的遍历 4. vector 的扩容机制 5. vector 的空间接口 5.1 resize 接口 5.2 push_back 5.3 insert 5.4 erase 5.5 流插入与流提取 vector 并不支持流插入与流提取&#xff0c;但是可以自己设计&#xff0c;更…

[NSSCTF 2022 Spring Recruit]ezgame

打开题目环境是一个游戏: 直接F12开始审计源代码&#xff1a; 这里说拿到65分以后可以得到flag&#xff0c;这里因为游戏太菜选择直接篡改分数&#xff1a; 在控制台输入scorePoin10000就可以改变当次得分&#xff0c;等到游戏结束就能得到flag。

心觉:成功学就像一把刀,有什么作用关键在于使用者(二)

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作174/1000天 上一篇文章讲了成功学到底是个啥 是如何起作用的 为什么有些人觉得没有用&#xff1f; 今天我们再展开来剖析一下这…

链表--(1)链表的概念

前言引入 之前我们学习了数组这一概念,使用数组可以在编程时增加程序的灵活性。但在c语言中不允许定义动态数组的类型也不能随意调整数组的大小,往往会导致内存空间的浪费。由此我们推出链表。链表是动态进行内存分配的一种结构,它可以随时为其结点分配需要的存储空间也方便…

Vscode搭配latex简易教程

1. 找镜像网站下载texlive的iso文件 清华源镜像 下载之后直接打开iso文件&#xff0c;打开install-tl-windows.bat文件&#xff0c;进行安装即可&#xff0c;安装大概30分钟左右 2. VScode端配置 2.1 下载这三个插件 2.2 打开设置 2.3 追加内容到配置json文件当中 // Latex…

《深入理解JAVA虚拟机(第2版)》- 第12章 - 学习笔记

第12章 Java内存模型与线程 12.1 概述 TPS是用来衡量一个服务性能好坏高低的重要指标值。TPS是Transactions Per Second的缩写&#xff0c;用来表示每秒事务处理数&#xff0c;即服务端每秒平均能碰响应的请求数。 12.2 硬件的效率与一致性 处理器与内存的运算效率差了好几…

使用阿里OCR身份证识别

1、开通服务 免费试用 2、获取accesskay AccessKeyId和AccessKeySecret 要同时复制保存下来 因为后面好像看不AccessKeySecret了 3.Api 参考 https://help.aliyun.com/zh/ocr/developer-reference/api-ocr-api-2021-07-07-recognizeidcard?spma2c4g.11186623.0.0.7a9f4b1e5C…

园区网基础组网保姆级(mstp,vrrp,irf,eth-trunk,route-policy,ospf,bgp,rbm,nat,mlag等等)

本文实验使用模拟器:H3C HCL 5.10.2版本 一、园区核心/接入架构1.1.三层架构1.2.二层架构二、园区核心 To 接入实践2.1.MSTP+VRRP派系2.1.1.MSTP+VRRP配置2.1.2.MSTP+VRRP验证2.2.IRF+Eth-Trunk派系2.2.1.IRF+Eth-Trunk配置2.3.两种派系的对比2.4.VXLAN结构三、园区核心/出口架…

观《中国数据库前世今生》有感:从历史中汲取未来的力量

观《中国数据库前世今生》有感&#xff1a;从历史中汲取未来的力量 中国数据库技术的起步与发展 观看了《中国数据库前世今生》后&#xff0c;我对于中国数据库技术的历史变迁有了更深刻的理解。作为一名有一年开发经验的程序员&#xff0c;这部纪录片让我对中国数据库行业从8…

828华为云征文 | 云服务器Flexus X实例,搭建上线前后端项目

828华为云征文 | 云服务器Flexus X实例&#xff0c;搭建上线前后端项目 项目搭建 演示使用华为云服务器Flexus X实例搭建上线前后端项目黑马vue电商后台管理系统 项目GitHub链接&#xff1a;https://github.com/Minori-ty/vue_shop.git 1、购买华为云 Flexus X 实例 Flexus云服…

.Net Core 生成管理员权限的应用程序

创建一个ASP.NET Core Web API项目 给解决方案设置一个名称 选择一个目标框架&#xff0c;这里选择的是 .NET 8.0框架 在Porperties文件夹中添加一个app.manifest文件 设置app.manifest文件属性&#xff0c;生成操作设置为嵌入的资源 双击解决方案名称&#xff0c;编辑WebAppli…

JS实现树形结构数据中特定节点及其子节点显示属性设置的技巧(可用于树形节点过滤筛选)

大家好&#xff0c;今天我要分享的是如何在树形结构的数据中&#xff0c;根据特定条件设置节点及其所有子节点的显示属性。在实际项目中&#xff0c;这种需求非常常见&#xff0c;特别是在需要动态展示和隐藏节点的情况下。下面我将通过一个具体的示例来讲解实现过程。 需求分析…

(史上最全)线程池

线程池 文章目录 线程池一&#xff0c;前言二&#xff0c;线程池三&#xff0c;参数四&#xff0c;线程池的实现原理5.线程池的使用案例(自定义线程池)6.使用Executors 创建常见的功能线程池1.固定大小线程池2.定时线程3.可缓存线程池4.单线程化线程池 一&#xff0c;前言 虽然…

Prometheus 上手指南

文章目录 Prometheus 相关概念Prometheus 的特点Prometheus 架构数据模型 Datemode使用场景 指标类型 Metric type适用场景 作业和实例 Jobs and instances使用场景 Prometheus 安装Prometheus 配置prometheusalertmanager Grafana 可视化Grafana 安装Grafana 配置选项Grafana …

假期学习--iOS 编译链接

iOS 编译链接 编译流程 四步&#xff1a; 1.预处理 2.编译 3.汇编 4.链接 大概的步骤如下&#xff1a; 预处理 作为编译的第一步&#xff0c;将.m文件转换为.i文件 &#xff1b; 预处理是要处理源代码中所有以#开头的所有预编译指令 &#xff1b; 规则如下&#xff1…

更换UFS绑定固件与“工程固件”的区别 小米10s机型更换cpu绑定包对比 写入以及修复基带

目前机型的安全机制越来越高。机型cpu与字库存在绑定关系。主板cpu如损坏需要更换。换新cpu后就需要刷写底层绑定包来修复 。今天的博文将为大家带来UFS绑定包与工程固件的区别以及写入 修复基带的步骤解析 通过博文了解 1💝💝💝-----更换UFS绑定包与工程固件的区别 2…

利士策分享,赚钱与体重:一场关于生活平衡的微妙探索

利士策分享&#xff0c;赚钱与体重&#xff1a;一场关于生活平衡的微妙探索 在当今社会&#xff0c;赚钱与体重&#xff0c;这两个看似风马牛不相及的概念&#xff0c; 却在无形中交织着人们的生活轨迹。 它们不仅仅是数字上的增减&#xff0c;更是个人选择、生活方式乃至心理…

Mycat搭建分库分表

分库分表解决的问题 单表数据量过大带来的性能和存储容量的限制的问题&#xff1a; 索引效率下降读写瓶颈存储容量限制事务性能问题分库分表架构 再搭建一对主从复制节点&#xff0c;3307主节点&#xff0c;3309从节点配置数据源 dw1 , dr1,创建集群c1创建逻辑库 CREATE DATAB…

【AI小项目6】QLoRA针对资源受限设备上的大模型微调和文本分类任务的推理

目录 一、项目简介概述时间主要工作和收获技术栈数据集结果参考 二、训练阶段的完整代码什么是 QLoRA 微调&#xff1f;注意 安装库导入包配置定义一个Config类配置训练参数配置Lora 初始化分词器和模型数据预处理计算模型评估指标交叉验证划分数据集举例&#xff1a; 创建Trai…