C生万物 | 校招热门考点 —— 结构体内存对齐

在这里插入图片描述

在这里插入图片描述

文章目录

  • 一、前言
    • 结构体偏移量计算:offsetof
  • 二、规则介绍
    • 例题的分解与细说
  • 三、习题演练
    • 1、练习①
    • 2、练习②
  • 四、为什么存在内存对齐?
    • 1、平台原因(移植原因)
    • 2、性能原因
  • 五、如何修改默认对齐数
  • 六、实战演练
    • ✍一道百度笔试题: offsetof 宏的实现
    • 💬两道高频面试题
  • 七、总结与提炼

一、前言

在结构体章节,我们掌握了结构体的基本使用,但是现在我要你去计算一个结构体的大小,你会怎么做呢?

  • 现在我定义了两个结构体,通过观察可以发现它们内部的成员变量都是一样的,均有c1c2i三个成员变量,那此时分别去计算它们两个结构体的大小, 最后的结果会是多少呢?会是一样的吗
struct S1 {
	char c1;
	int i;
	char c2;
};

struct S2 {
	char c1;
	char c2;
	int i;
};

int main(void)
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}
  • 通过运行可以发现两者是不一样的,这是为什么呢?如果你没有结构体内存对齐的相关知识,那相信你一定会这么去计算:
    • 在结构体S1中,c1的类型为【char】,是1个字节;
    • i的类型是【int】,是4个字节
    • c2的类型为【char】,是1个字节;
  • 那么最后的结果就是1 + 4 + 1 = 6B,可事实呢,原不止这些。。。

在这里插入图片描述

结构体偏移量计算:offsetof

  • 就上面这么来看还是看不出什么细节的内容,给读者介绍一个宏叫做offsetof,它可以用来计算结构体成员相对于起始位置的偏移量

在这里插入图片描述
它的第一个参数是结构体类型,第二个参数是结构体成员

printf("%d\n", offsetof(struct S1, c1));
printf("%d\n", offsetof(struct S1, i));
printf("%d\n", offsetof(struct S1, c2));
  • 最后,计算出来的结果分别是【0】【4】【8】,那我们可以通过画内存图来看看结构体中的三个成员变量在内存中究竟是如何分布的
  • 可以看出,因为总的结构体大小为12B,可是在放完这3个成员后中间空出了三个位置,并且对于最后在c放完之后还没有到达12B,所以还得再浪费3个空间的废位置
    在这里插入图片描述

为什么会出现上面这样的现象呢?对于结构体内存对齐的规则是怎样,让我们继续看下去👇

二、规则介绍

  1. 第一个成员在与结构体变量偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
    • VS中默认的值为8、Linux环境默认不设对齐数(对齐数是结构体成员自身的大小)】
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

例题的分解与细说

知晓了上面这些规则后,我们再来回顾一下上面这个结构体的大小该如何计算

  • 假设我这里创建一个结构体变量叫做ss,它的起始地址就从0开始,所以根据第一条规则,第一个成员变量在与结构体变量偏移量为0的地址处,而且它的类型还是char,所以只占1个内存单元

在这里插入图片描述

  • 接下去看第二个成员变量i,其为整型所以在内存中就需要存储4个字节的大小,此时便要拿其和VS下默认对齐数8去进行比较,取较小的值4
  • 算出来【4】之后便要对齐到4整数倍的地址处,那就是4这块空间,往下一直占用4个字节,这就是成员变量i在这个结构体中的内存占用分布
  • 那既然这个i是从4的位置开始放的,中间空出来的位置就不会再放置其他成员变量了,那么这个3个空间也就浪费了

在这里插入图片描述

  • 接下去放置第三个成员变量c2,char类型的变量为1个字节,和8比较取小就是1,那就要将其放到1整数倍的地址处,那其实任何空间都是可以的,直接放到这个【8】的位置就行
  • 那截止目前为止这个结构体中的所有成员变量都放置完了,此时去计算一个所占的内存空间就可以发现只有9个字节。但是在一开始我们计算的这个结构体的大小为12个字节,可是现在还差3个字节,所以最后就要去进行一个填充。但是,为什么呢?

在这里插入图片描述

  • 这就要用到第三条规则了:结构体总大小为最大对齐数的整数倍
  • 那在这么计算下来之后,就可以知道结构体中的最大对齐数为4,那么【9】、【10】、【11】都不是它的整数倍,只有【12】是它的整数倍的地址处(注意这里是地址处!),因此我们需要填充3个字节,此时从0 ~ 11就有12个字节了,便为4的整数倍 👉这就是【12】如何被计算出来的全过程,你听懂了吗?

在这里插入图片描述


看完了,这个结构体后,还记得结构体S2吗,我再来讲一道,当然你也可以试着自己写写画画看👈

  • 首先还是一样,c1放在这个与结构体变量偏移量为0的地址处,而且它的类型还是char,所以只占1个内存单元
    在这里插入图片描述
  • 接下去还是一样,在放置第二个成员变量开始就要考虑【对齐数】了,char所占的字节为1B,与8去进行比较一下就可以知道1来得小,那我们直接放在偏移处为1的地方就可以了,此时在内存中也只占了2个字节

在这里插入图片描述

  • 接下去放置第三个成员变量【i】,大小为4个字节小于8因此选择在4的整数倍的地址处开始放置这个变量,整型占4个字节,所以一直占用到偏移量为7的地方
  • 接下去就是计算整个结构体的大小,最大对齐数为4,所以要为4的整数倍,此时去计算一下得知从0 ~ 7偏移了7个字节,占用了8个空间,刚好为4的整数倍,所以结构体S2的大小为【8】是这么算出来的,你明白了吗?
    在这里插入图片描述

三、习题演练

通过上面两道例题的讲解,相信你对如何去计算结构体大小一定有了一个自己的认识,接下去就让我们趁热打铁🔥来做两道题目再练一练,看看自己是否真的掌握了

1、练习①

你可以先试着自己做一做,然后和我对一下是否正确

struct S3
{
	double d;
	char c;
	int i;
};
printf("%d\n", sizeof(struct S3));

【分析】:

  • 首先看到第一个成员变量,从偏移量为0的地址处开始放起,因为double类型的数据在内存中占8个字节,所以一直占用偏移处为7的地方

在这里插入图片描述

  • 对于第二个成员变量【c】,类型为char,所以在内存中占用1个字节,那直接放在偏移量为8的地址处即可
    在这里插入图片描述
  • 接下去来安排第三个成员变量【i】,整型占用4个字节,比VS下默认对齐数8来得小所以【对齐数为4】,去寻找4整数倍的地址处,【9】、【10】、【11】都不是,【12】是4的整数所偏移的地址处,从此处开始往下数4个字节的空间,刚好放满15
  • 最后我们便去计算整个结构体的大小,为最大对齐数的整数倍,最大对齐数是8,计算一下放置三个成员变量占了16个空间,刚好是8的整数倍,因此16即为结构体的大小

在这里插入图片描述
运行结果如下:

在这里插入图片描述
也可以通过【offsetof】来验证一下

在这里插入图片描述

2、练习②

接下去再来做一道练习,涉及结构体嵌套的问题,对应的需要使用到规则4,忘记了可以翻上去看看👈

struct S3
{
	double d;
	char c;
	int i;
};

struct S4
{
	char c1;
	struct S3 s3;	//成员变量为另一个结构体
	double d;
};

因为本题的结构体比较大,所以就标出4的整数倍所在的地址

  • 首先还是一样,来看到第一个成员变量【c1】,放到与结构体变量偏移量为0的地址处,又因为类型为char,所以只占一个字节的空间

在这里插入图片描述

  • 接下去,就是嵌套的结构体s3,此时我们要对齐到s3这个结构体中最大对齐数的整数倍处,那么最大对齐数就是【8】,所以要从8的地址处开始往下放置,那要占用多少空间呢?这就是s3这个结构体的大小【16】,所以一直往下数16个空间即可,一直到23这个地址处
  • 那么中间的这7个位置就算是浪费了👈

在这里插入图片描述

  • 最后就是这个【d】,与VS中的默认对齐数一致,所以为【8】,下一个24刚好为8整数倍的地址处,所以从这开始放,double类型的数据在内存中占8个字节,所以一直到31的地址处
  • 然后来算整个结构体s4的大小,为所有最大对齐数(含嵌套结构体的对齐数)的整数倍,也就是取s3和s4中的最大对齐数,那也就是【8】,计算一下结构体s4所占的内存空间为32,刚好为8的整数倍,所以整个结构体的大小即为32

在这里插入图片描述

运行结果如下:

在这里插入图片描述
可以通过【offsetof】再来验证一下

在这里插入图片描述

四、为什么存在内存对齐?

经过了两道例题和两道练习题的训练,相信你对如何计算结构体的大小一定是心中有数了,但在阅读的过程中你是否有疑惑为什么会存在这个【结构体内存对齐】呢?有什么实际意义吗?

1、平台原因(移植原因)

  • 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常

2、性能原因

  • 假设下面有一个结构体,内部有两个成员变量ci,然后要在内存中存储它们,我分为了两种,一个是【无内存对齐】,呈现的是紧密存放;一个是【内存对齐】,需要考虑到最大对齐数
  • 然后在32位平台下去分别访问结构体中的成员,假设现在读取数据的时候一次性读四个字节。
    • 首先看到的是【无内存对齐】的结构体内存分布,读一次就能读到c,但是若要全部读取完i,就还需要再读取一次,那访问到所有的成员变量就需要两次;
    • 接下去看到的是【内存对齐】的结构体内存分布,因为内存对齐的缘故,所有两个成员变量ci互不干扰,此时再看到成员变量i,从它的初始地址处开始读取,一次读4个字节,那么读1次就刚刚好可以读完这个变量了,而不是像上面那样还需要再读一次

在这里插入图片描述

  • 所以原因就在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说:

结构体的内存对齐是拿空间来换取时间的做法


了解了为什么会存在内存对齐之后,我们再回到一开始的这两个结构体,你是否有想过为什么两个结构体的成员变量都一模一样但是大小却是一个【12】,一个【8】呢?

  • 没错,就是你想到的它们所存放的位置不一样罢了。因为要存在内存对齐,所以若两个对齐数大的成员变量定义在一起的话为了满足规则就可能会浪费很多空间的内存。
  • 但若是两个对齐数较小甚至相同规定的变量定义在一块的话,可能它们就是挨着放的,占用的空间少了↓,那最后结构体的大小就变小了
struct S1 {
	char c1;
	int i;
	char c2;
};

struct S2 {
	char c1;
	char c2;
	int i;
};
  • 所以,那在设计结构体的时候,我们既要满足对齐,又要节省空间,就要让占用空间小的成员尽量集中在一起

五、如何修改默认对齐数

之前我们见过了 #pragma 这个预处理指令#pragma comment,用来链接函数的静态库。这里我们再次使用,可以改变我们的默认对齐数

  • 用法很简单#pragma pack(1)就可以设置默认对齐数为1,#pragma pack()就可以取消设置的默认对齐数,还原为默认。到它为止的默认对齐数还是被修改后的对齐数
  • 接下去就来看下面这个修改完默认对齐数后的结构体,它的大小会是多少呢?
#pragma pack(1)//设置默认对齐数为1
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S1));
	
	return 0;
}
  • 可以看到,若是默认的对齐数设置为1的话,那其实可以看出每个成员变量的对齐数就都是1了,那么也就不存在浪费的现象,因为任何数都是1的整数倍,所以3个成员变量的内存分布如下,大小即为【6】

在这里插入图片描述
运行结果如下:

在这里插入图片描述

可以通过【offsetof】再来验证一下

在这里插入图片描述
结论:

  • 结构在对齐方式不合适的时候,我么可以自己更改默认对齐数

六、实战演练

✍一道百度笔试题: offsetof 宏的实现

在上面的每一个结构体计算后,我都使用到了offsetof这个宏,和我画出来的内存分布图完全就是一致的,那它的原理到底是怎样的呢?马上来探究一下🔍

曾经有一年的百度笔试题就考到了有关offsetof 的实现原理

👉 【原题】:写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明

  • 那要如何去实现呢?如果对宏不是很了解的读者可以看看详解程序环境和预处理

  • 我们通过上面的结构体S1进行讲解。列出3个成员变量放置的初始地址,其实【offsetof】计算的也就是每个变量在内存中的起始地址相较于首地址偏移了多少,那将它们进行一个相减就可以得出048这三个结果

在这里插入图片描述

  • 但是上面的这些地址太复杂了,都是十六进制的,接下去我们来将c1这块地址设置为0,那么
    • 【c1】相对于自己的偏移量就是&c1 - 0
    • 【i】相对于自己的偏移量就是&i - 0
    • 【c2】相对于自己的偏移量就是&c2 - 0
  • 但其实这可以看出,虽然每个成员变量各自的偏移量为他们的地址减去首地址,但是可以看出这减了和没减有什么区别呢?所以可以得出它们三者的偏移量其实就是他们各自的初始地址

在这里插入图片描述


知道了上面这些我们就可以使用【宏】来实现每个成员变量偏移量的计算了

#define OFFSETOF(m_type, m_name)	(int)&(((m_type *)0)->m_name)
  • 不过相信你一定看不懂上面这个宏,所以我会来一步步讲解一下

m_type是结构体变量;m_name是结构体成员

  • 首先是地址为0的这个地方要放置结构体成员,但是0是一个整型,所以我们使用强制类型转换将0转换成一个结构体的指针,那么在外部传入结构体成员变量的时候就符合类型了
#define OFFSETOF(m_type, m_name)	(m_type *)0

printf("%d\n", OFFSETOF(struct S1, c1));
  • 那既然这是一个结构体指针的话,就可以访问到其内部的结构体成员变量,也就是这个m_name
#define OFFSETOF(m_type, m_name)	((m_type *)0)->m_name
  • 那么在上面说到过,每个结构体成员变量的地址就是它相对于起始位置的偏移量
#define OFFSETOF(m_type, m_name)	&(((m_type *)0)->m_name)
  • 但是呢,在打印的时候可以看出对于偏移量而言都是第一个整数,所以还要对取到的地址偏移转换为整型,便是最后的结果
#define OFFSETOF(m_type, m_name)	(int)&(((m_type *)0)->m_name)

下面是流程图:

在这里插入图片描述
下面是运行结果:

在这里插入图片描述

💬两道高频面试题

结构体怎么对齐? 为什么要进行内存对齐?

  • 结构体内存对齐存在对应的规则,规则如下
    1. 第一个成员在与结构体变量偏移量为0的地址处
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
      对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
      • VS中默认的值为8、Linux环境默认不设对齐数(对齐数是结构体成员自身的大小)】
    3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
  • 为什么要进行内存对齐呢?原因有两个,一个是平台本身的原因,任意地址上的任意数据是不能随意访问的,如果不正确访问可能会造成硬件异常。第二个就是性能原因,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

如何让结构体按照指定的对齐参数进行对齐?能否按照3、4、5即任意字节对齐?

  • 可以的,只需要使用一个预处理指令#pragma pack(3)便可以将默认对齐数修改为3,其他的也是同理,因为结构体默认对齐数发生了变化,此时就会导致结构体大小发生变化

七、总结与提炼

最后来总结一下本文所学习的内容📖

  • 在本文中,主要是介绍了如何去计算一个结构体的大小,最重要、最核心的还是开头的4条规则,我们再来回顾一下
  1. 第一个成员在与结构体变量偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
    • VS中默认的值为8、Linux环境默认不设对齐数(对齐数是结构体成员自身的大小)】
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
  • 有了规则之后,将它们灵活地运用到实际的题目中,只要掌握了方法,就感觉其实计算结构体的大小也没有那么复杂,就是对于【嵌套结构体】的规则有些复杂,要考虑到另一个结构体中的最大对齐数
  • 接下去,我们就谈到了为什么在计算这些结构体的时候会存在内存对齐的现象,对于了设置与不设置内存对齐便观察到这是【空间换时间】的做法
  • 谈了很久的offsetof(),但是不清楚原理是什么👉这不,百度笔试题就考到了,于是我们就去自己通过一个宏实现了一下这个偏移量的求解,虽然过程很复杂,但是在我一步步的细讲下,相信聪明的你一定有所理解😁在理解了结构体内存对齐的各方面之后,面对两道面试题也是毫不畏惧💪

可以发现仅仅是非常小的一个知识点,我却讲解了近万字,因为这是校招的笔试面试中C语言这一块的热门考点,如果有投递相关岗位的同学一定要搞清楚每一步🥰

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自己设计的网站,如何实现分页功能?(详细代码+注释)

目录 前言 实现分页功能 需求分析 客户端开发 服务器开发 前后端交互——两种前端得到 文章总页数 的方法,那种更合适? 前言 你在设计网站的时候是否有过这样的烦恼:“我设计的网站怎么就是从上到下一条线内容全部展开,一点都…

IDEA 热部署,修改代码不用重启项目

热部署指在修改项目代码的时候不重启服务器让修改生效。安装JRebel and XRebelFile->Settings,然后Plugins-> Marketplace,输入JRebel,安装如下插件——JRebel and XRebel ,重启idea激活JRebel and XRebel第一行输入网址&am…

linux入门---环境变量

目录标题指令的本质如何不加./方法一方法二环境变量的重置在命令行上查看环境变量为什么会存在环境变量在程序中查看环境变量本地变量和环境变量环境变量的继承指令的本质 在使用linux的时候我们经常会使用很多指令比如说:ll指令,pwd指令,wh…

Java JDK详细安装配置(详细备忘版本)

目录概览一、下载安装二、环境配置三、常见问题一、下载安装 官方下载地址:点我去官网 java20 、java17如下: java8、java11如下 jre8 如下 以 java8 下载为例: 按步骤输入账号密码 之后就会跳出下载显示框 得到了文件名为 jdk-8u361-win…

单机分布式一体化是什么?真的是数据库的未来吗,OceanBase或将开启新的里程碑

一. 数据 我们先说说数据这个东西,这段时间的ChatGPT在全世界的爆火说明了一件事,数据是有用的,并且大量的数据如果有一个合适的LLM大规模语言模型训练之后,可以很高程度的完成很多意想不到的事情。 我们大多数的时候的注意力只…

class03:MVVM模型与响应式原理

目录一、MVVM模型二、内在1. 深入响应式原理2. Object.entries3. 底层搭建一、MVVM模型 MVVM,即Model 、View、ViewModel。 Model > data数据 view > 视图(vue模板) ViewModel > vm > vue 返回的实例 > 控制中心, 负责监听…

ChatGPT使用介绍、ChatGPT+编程、相关组件和插件记录

文章目录介绍认识ChatGPT是通过英汉互译来实现中文回答的吗同一个问题,为什么中英文回答不同ChatGPT的使用对话组OpenAI APIAI智能绘图DALLE 2ChatGPT for Google插件ChatGPT编程编写代码代码错误修正与功能解读代码评审与优化推荐技术方案编写和优化SQL语句在代码编…

Spring Boot集成RocketMQ实现普通、延时、事务消息发送接收、PULL消费模式及开启ACL | Spring Cloud 30

一、前言 在前面我们通过以下章节对RocketMQ有了基础的了解: docker-compose 搭建RocketMQ 5.1.0 集群(双主双从模式) | Spring Cloud 28 docker-compose 搭建RocketMQ 5.1.0 集群开启ACL权限控制 | Spring Cloud 29 现在开始我们正式学习…

蓝桥杯 - 求组合数【C(a,b)】+ 卡特兰数

文章目录💬前言885. 求组合数 I C(m,n) 【dp】886 求组合数 II 【数据大小10万级别】 【费马小定理快速幂逆元】887. 求组合数 III 【le18级别】 【卢卡斯定理 逆元 快速幂 】888.求组合数 IV 【没有%p -- 高精度算出准确结果】 【分解质因数 高精度乘法 --只用一…

5.5G产业再提速!高通5GAdvanced-ready芯片商用终端下半年面世

MWC2023大会召开在即,5GAdvanced产业再添重磅消息!2月15日,高通宣布推出全球首个5GAdvanced-ready基带芯片——骁龙X755G调制解调器及射频系统,支持毫米波和Sub-6GHz频段,带来网络覆盖、时延、能效和移动性等全方位的提…

【C语言】深度理解指针(下)

一. 前言💎昨晚整理博客时突然发现指针还少了一篇没写,今天就顺便来补一补。上回书说到,emmm忘记了,没事,我们直接进入本期的内容:本期我们带来了几道指针相关笔试题的解析,还算是相对比较轻松的。话不多说…

RTL8201 以太网PHY芯片 调试记录

一、概述 为了尽量给甲方降低成本,决定使用较低成本的PHY芯片RTL8201F-VB-CG芯片。移植官网的以太网demo程序,git上下载了一份很好看的rtl8201F的驱动程序,用来替换官方demo的lan8742程序。并没有直接通,于是开始了调试之路。 二…

mysql索引类型有哪些?

在Mysql数据库当中,我们经常会谈到Sql语句,当然也会谈到索引优化,那么在数据库当中有哪些索引类型呢,博主在这里进行分享,希望对大家能有所帮助。 目录 1、B-Tree索引: 2、Hash索引: 3、Full…

SpringBoot 将PDF转成图片或World

SpringBoot 将PDF转成图片或World 准备工作Apache PDFBox将PDF转成一张图片将PDF转成多张图片将PDF转成其他文件格式总结SpringBoot 是一款非常流行的 Java Web 开发框架,可以用来构建各种 Web 应用程序。在本篇博客中,我们将介绍如何使用 SpringBoot 将 PDF 转换成图片或其他…

Elasticsearch 学习+SpringBoot实战教程(三)

需要学习基础的可参照这两文章 Elasticsearch 学习SpringBoot实战教程(一) Elasticsearch 学习SpringBoot实战教程(一)_桂亭亭的博客-CSDN博客 Elasticsearch 学习SpringBoot实战教程(二) Elasticsearch …

第十四届蓝桥杯三月真题刷题训练——第 23 天

目录 第 1 题:长草 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码: 思路: 第 2 题:蓝肽子序列_LCS_最长公共子序列dp问题 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码: 思路&am…

Spring源码面试最难问题——循环依赖

前言 问:Spring 如何解决循环依赖? 答:Spring 通过提前曝光机制,利用三级缓存解决循环依赖(这原理还是挺简单的,参考:三级缓存、图解循环依赖原理) 再问:Spring 通过提前…

【前沿技术】问答pk【ChatGPT Vs Notion AI Vs BAT AI 】

目录 写在前面 问题: 1 ChatGPT 1.1 截图 ​1.2 文字版 2 Notion AI 2.1 截图 2.2 文字版 3 BAT AI 3.1 截图 3.2 文字版 总结 序言 所有幸运和巧合的事,要么是上天注定,要么是一个人偷偷的在努力。 突发奇想,问三个…

③【Java组】蓝桥杯省赛真题 持续更新中...

个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ 蓝桥杯真题--持续更新中...一、错误票据题目描…

CCF-CSP认证 202303 500分题解

202303-1 田地丈量&#xff08;矩阵面积交&#xff09; 矩阵面积交x轴线段交长度*y轴线段交长度 线段交长度&#xff0c;相交的时候是min右端点-max左端点&#xff0c;不相交的时候是0 #include<bits/stdc.h> using namespace std; int n,a,b,ans,x,y,x2,y2; int f(in…