【C语言】结构体超详细全讲解 (代码+万字文字+画图讲解)

news2024/11/23 12:42:52

dcebcb7976884652a4fdad7708b196ea.png

目录

1.什么是结构体类型

2.结构体变量的创建

 3.结构体变量的初始化

4.结构体的特殊声明

5.typedef重定义结构体变量(两种方式)

 6.结构体自引用

 7.创建结构体指针变量

8.结构体内容的访问

8.1直接访问:

8.2 结构体指针访问

9.结构体内存存储 

9.1 结构体内存存储4条规则

例题1&&讲解:

结构体内容的储存方式:

结构体字节的大小:

 例题2&&讲解:

结构体内容的储存方式:

结构体字节的大小:

例题3&&讲解:

 结构体内容的储存方式:

结构体字节的大小:

例题4&&讲解(结构体嵌套问题 )

 结构体内容的储存方式: 

结构体字节的大小:

 10.为什么存在内存对齐?

 11.如何节省结构体内存大小

 12.修改默认对齐数

 13.scand初始化结构体变量

 错误写法如下​编辑:

14.结构体传参

​编辑


小心!VS2022不可直接接触,否则!没这个必要,方源面色淡然一把抓住!顷刻炼化! 


 

1.什么是结构体类型

什么是结构体类型,这里拿一个常见的格式举例:

struct stu  //struct stu 为结构体类型
{
    int a; //结构体内容
}s1;    //s1 为结构体变量

 这是一个很常见的结构体格式

struct stu 为结构体类型

s1 为结构体的变量

int a 为结构体变量的内容


2.结构体变量的创建

结构体变量的创建有两种方法,一种是随着结构体的创建而创建

另一种则是单独创建,示例如下:

方法一:

struct stu1{
    int a;
}s1;    //创建结构体变量s1

 方法二(含注意事项):

struct stu{
int a;
};  //注意这里的 ; 一定要写,不写就是错误的结构体

struct stu s1;    //创建结构体变量s1

 这里采用的是 用结构体类型创建结构体变量

不理解的可以类比int a,类型加变量名


 3.结构体变量的初始化

 结构体变量的初始化可以分为三种情况:


情况1&&情况3:
    变量随着结构体而定义
    定义结构体变量需要采用,变量名.结构体内容 进行初始化结构体内容

情况1:
	变量随着结构体而定义
	定义结构体变量需要采用,变量名.结构体内容 进行初始化结构体内容


示例如下
(情况一):
struct stu  //结构体类型为struct stu
{
	int a;
	char arr[20];
	int d;
}s1, s2;   //结构体变量为s1,s2

//结构体初始化:
s1.a = 20;//相等于直接访问结构体内容,并直接赋值
s1.arr[0] = 'w'; //对单个字符型数组存储'w'
printf("%c \n",s1.arr[0]);//打印字符w
printf("%d ",s1.a);//打印20
------------------------------------------------------
------------------------------------------------------
(情况三)直接将变量初始化
struct stu  //结构体类型为struct stu
{
	int a;
	char arr[20];
	int d;
}s1 = {20,"w",987};    //从数组起始地址开始存储"w"
//直接将变量初始化

 情况2:
变量不随着结构体而定义
定义结构体变量采用, 结构体类型+变量名 的形式进行初始化结构体内容

情况2:
变量不随着结构体而定义
定义结构体变量采用, 结构体类型+变量名 的形式进行初始化结构体内容

示例如下:情况2
struct stu  //结构体类型为struct stu
{
	int a;
	char arr[20];
	int d;
};
struct stu s1 = { 20,"w",987 }; //按照顺序初始化结构体内容
struct stu s2 = { .a = 90,.d = 98799,.arr[0] = 'd' };
//指定顺序初始化,采用 .结构体内容的形式 初始化

printf("%d %c %d\n", s1.a, s1.arr[0], s1.d);
//打印20 w 987
printf("%d %c %d\n", s2.a, s2.arr[0], s2.d);
//打印90 d 98799

4.结构体的特殊声明

如果结构体声明不完全,创建的结构体只能使用一次,示例如下:

struct {
//结构体未完全声明
	int a;
	int b;
}s1;
s1.a = 20;
s1.b = 30;
//未声明的结构体属于匿名结构体类型,只能使用一次

s2.a = 40;
//使用完变量s1后再想创建新的变量s2
//但是因为是匿名结构体类型,所以系统会报错,不能再次创建和使用新的变量

5.typedef重定义结构体变量(两种方式)

 第一种方式:

typedef struct stu node;//将结构体类型 struct stu 重定义为 node
   	struct stu 
    {
		int a;
	};
	node s1 = { 20 };  //这里的node就相当于struct stu
	printf("%d ", s1.a); //这里是结构体的直接访问,变量名.结构体内容,可以直接访问到结构体内容

	//创建结构体指针
	node * p1 = &s1;    //后面讲

第二种方式:

typedef struct stu 
{
	int a;
}node;  //这里不是创建结构体变量node,而是将struct stu重定义为node
node s1 = { 20 };    //这里的node就相当于 struct stu
printf("%d ", s1.a); //直接访问结构体变量s1中的a,打印20


 6.结构体自引用

结构体自引用是一件很可怕的事,结构体最怕的就是重命名,一旦重命名就会出现错误

比如说结构体的变量与结构体的内容重名就会出现错误

那么什么是结构体的自引用呢?结构体的自引用就是一个结构体嵌套自己的结构体

示例如下:

struct stu 
{
	int a;
	struct stu s1;
	//死循环,变量s1里还有结构体,又会创建新的变量s1,空间无限大
};

 使用自己的结构体嵌套自己,就会出现无数多个自己的结构体,结构体的内存就会无限大,是不合理的


 7.创建结构体指针变量

对于结构体来说,结构体在未创建变量时不占用内存

所以结构体的变量名会表示整个结构体,因此&结构体变量名,就相当于取出了整个结构体变量的内存

那么如何创建结构体指针变量呢?示例如下:

 创建结构体指针变量第一种写法:

struct stu {
	int a;//创建结构体指针变量p1,指针变量p1的类型为struct stu *
}s1; 

//创建结构体指针变量 p1,存放结构体变量s1的地址
struct stu* p1 = &s1;
//结构体指针变量p1的类型是struct stu*

 相信有的同志会写出这样的代码:

struct stu {
	int a;//创建结构体指针变量p1,指针变量p1的类型为struct stu *
}s1; struct stu * p1 = &s1;
printf("%p\n", &s1);//打印012FFA30
printf("%p\n", p1); //打印012FFA30

其实这样写也是对的,因为在结构体大括号外的 ; 写完之后,再写句子就是单独的表达式了,所以其实和第一种写法是完全相同的,只是代码放的位置不一样

创建结构体指针变量第二种写法:

struct stu {
	int a;//创建结构体指针变量p1,指针变量p1的类型为struct stu *
}*p; 


8.结构体内容的访问

结构体内容的访问可分为两种,一种为直接访问,另一种为通过结构体指针访问

8.1直接访问:

struct stu  //结构体类型为struct stu
{
	int a;
	char arr[20];
	int d;
}s1 = { 20,"w",98};

printf("%d %c %d", s1.a, s1.arr[0], s1.d);
//变量名.结构体成员
//打印20 w 98

8.2 结构体指针访问

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
int main()
{	
	struct stu  //结构体类型为struct stu
	{
		int a;
		char arr[20];
		int d;
	}s1 = { 20,{"w"},98 };
	struct stu* p1 = &s1; //将结构体变量s1的地址 存放进p1中

	printf("%d %d %c",p1->a,p1->d,p1->arr[0]);
    //结构体指针->结构体成员
	//打印20 98 w

9.结构体内存存储 

9.1 结构体内存存储4条规则

结构体内存储存方式有以下4条规则,也叫做结构体内存对齐原则
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数    = 编译器默认的第一个对齐数与该成员变量大小的较小值
vs中默认的值为8
Linux中gcc没有默认对齐数,对齐数就是成员自身的大小
3.结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍
4.如果嵌套了结构体,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍,结构体的整数大小就是所有最大对齐数(含嵌套结构体中成员的对齐数的整数倍)

多说无益,我们拿 代码例题+配图 来讲解

例题1&&讲解:

	//例题1:
	struct S1 
	{
	char c1;	
	int i;
	char c2;
	};
	printf("%zd ", sizeof(struct S1));
    //打印12

首先结构体的内存存储默认从0开始存储,我们以例题讲解

结构体内容的储存方式:

第一个结构体内容为char c1,结构体成员 c1 占用1个字节大小,结构体默认从0位置开始存储,那就从0开始存储1个字节大小的内存

第二个结构体内容为int i,结构体内容 i 占用4个字节,但因为结构体内存对齐原则,占用4个字节的int i,只能从 4 的整数倍数处(也叫结构体成员 i 的对齐数的整数倍)开始存储内存,所以我们直接跳到 4 的位置,从4的位置开始向下存储4个字节大小,4 5 6 7都是 i 的内存存储处

第三个结构体内容为char c2,结构体成员 c2 占用一个字节,因为结构体内存对齐原则,占用1个字节大小的char c2只能从1 的整数倍数处(对齐数的整数倍)开始存储内存,所以接着从 7 的位置向下存储1个字节大小的内存,即:将8的位置设为char c2 的内存存储处


结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为4,也就是int i 中的 i 的对齐数为4,

因为结构体内存存储占用了9个字节的大小,所以我们取4的整数倍数(最大对齐数)12,最终大小被填充到12个字节

7c148244ec734f7f8649a743576f0a03.jpeg

 例题2&&讲解:

	//例题2:
	struct S2 
	{
	char c1;	
	char c2;
	int i;
	};
	printf("%zd", sizeof(struct S2));
	//打印8

结构体内容的储存方式:

第一个结构体内容为char c1,结构体成员c1占用1个字节大小,结构体默认从0位置开始存储,那就从0开始存储1个字节大小的内存

第二个结构体的内容为char c2,结构体成员c2占用1个字节,但因为结构体内存对齐原则,占用1个字节的char c2,只能从 1 的整数倍数处(也叫c2 的对齐数的整数倍)开始存储内存,所以向下1个字节的位置为c2的内存存储地址

第三个结构体内容为int i,结构体成员 i 占用4个字节,因为结构体内存对齐原则,占用4个字节大小的int i 只能从4 的整数倍数处(对齐数的整数倍)开始存储内存,所以我们从 4 的整数倍处开始存储4个字节,4 5 6 7都为结构体变量内容 i 的存储地址

结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为4,也就是int i 中的 i 的对齐数为4,

因为结构体内存存储占用了8个字节的大小,所以我们取4的整数倍数(最大对齐数)8,最终大小被填充到8个字节

45adfa2174674b4da65667ef24817862.jpeg

例题3&&讲解:

	struct S3 
	{
		double d;
		char c;
		int i;
	};
	printf("%zd", sizeof(struct S3));
	//打印16

 结构体内容的储存方式:

第一个结构体内容为double d,结构体成员 d 占用8个字节大小,结构体默认从0位置开始存储,那就从0开始存储8个字节大小的内存

第二个结构体的内容为char c,结构体成员 c 占用1个字节,但因为结构体内存对齐原则,占用1个字节的char c,只能从 1 的整数倍数处(也叫c 的对齐数的整数倍)开始存储内存,所以向下1个字节的位置为 c 的内存存储地址

第三个结构体内容为int i,结构体成员 i 占用4个字节,因为结构体内存对齐原则,占用4个字节大小的int i 只能从4 的整数倍数处(对齐数的整数倍)开始存储内存,所以我们从 4 的整数倍处开始存储4个字节,因为8的位置已经被存储了,所以我们从下一个整数倍数--也就是12的位置开始存储,12 13 14 15都为结构体变量内容 i 的存储地址

结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为8,也就是double d 中的 d 的对齐数为4,

因为结构体内存存储占用了16个字节的大小,所以我们取最大对齐数 8 的整数倍数(最大对齐数)16,最终大小被填充到16个字节

58c6dc6b4839405a99b665ff8f6908cb.jpeg

例题4&&讲解(结构体嵌套问题 )

	struct S3 {
		double d;
		char c;
		int i;
	};
	//S3字节为16

	struct S4
	{
		char c1;
		struct S3 s3;
		double d;
	};
	printf("%zd ", sizeof(struct S4));
	//打印32

 结构体内容的储存方式: 

第一个结构体内容为char c1,结构体成员 c1 占用1个字节大小,结构体默认从0位置开始存储,那就从0开始存储1个字节大小的内存

第二个结构体的内容为struct S3 s3,结构体成员 s3 占用16个字节,但因为是结构体嵌套的内存存储,所以我们要从s3中成员最大对齐数的整数倍处开始存储,s3中最大对齐数的整数倍为8,所以我们从8的位置开始往下存储16个字节,直到23

第三个结构体内容为double d,结构体成员 d 占用8个字节,因为结构体内存对齐原则,占用8个字节大小的double d 只能从8 的整数倍数处(也叫对齐数)开始存储内存,所以我们从 8 的整数倍处开始存储8个字节,也就是从24的位置开始,向下存放8个字节大小的内存

结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为8,也就是double d 或者 s3中double d  的对齐数 8,

因为结构体内存存储占用了32个字节的大小,所以我们取最大对齐数 8 的整数倍数32,最终大小被填充到32个字节

e5f1b12faf7345308f55da5b363ded9c.jpeg


 10.为什么存在内存对齐?

大部分的参考资料都是这样说的:
1.平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的:某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对齐是拿空间来换取时间的做法。


 11.如何节省结构体内存大小

想要节省结构体内存的大小,我们需要同时满足对齐,又要节省空间

我们可以通过将占用空间小的结构体成员尽量集中在一起

例如:

struct S1 {
	char c1;
	int i;
	char c2;
};
//S1占用12个字节

struct S2 {
	char c1;
	char c2;
	int i;
};
//S2占用8个字节

S1和S2的结构体成员一模一样,但是S2占用的字节比S1小


 12.修改默认对齐数

在结构体中,我们可以使用一个预处理指令来修改结构体的默认对齐数

#pragma

#pragma这个预处理指令可以改变编译器的默认对齐数,这对于节省结构体内存非常有用

#pragma改变默认对齐数后,所有结构体内容的对齐数都会被#pragma修改

使用示例如下:

#pragma pack(1)//设置结构体默认对齐数为1
	struct S {
		char c1;
		int i;
		char c2;
	};

	struct S1 {
		double d;
		char c;
		int i;
	};
#pragma pack()//恢复结构体默认对齐数
	printf("%d\n", sizeof(struct S));
	//打印6
	printf("%d\n", sizeof(struct S1));
	//打印13

修改结构体默认对齐数后,所有结构体成员都会受影响,因此结构体成员的最大对齐数也会随之改变 

分析如图:

因为结构体成员的最大对齐数为1,所以结构体内存就以最大对齐数的整数倍(也就是 1 的整数倍)为结构体字节大小

027b6165cc294e1f84c61f8255f77f49.jpeg89d0313f7e134ed789740fc5df90ba1f.jpeg


 13.scand初始化结构体变量

使用scanf初始化结构体变量,就需要对变量内的内容直接访问,以初始化结构体变量内的内容

	struct stu {
		int a;
		char arr[20];
	}s1;
	scanf("%d%s", &s1.a, s1.arr);
	//s1.arr,代表的是s1中arr数组首元素的地址,所以不需要使用 & 操作符
	//输入20 abc
	printf("%d %s", s1.a, s1.arr);
	//打印20 abc

 错误写法如下079797e7364d4b95b6041ebdf45803b1.png

d3a13b0f06af41bf8e7b82802130e757.png

使用&s1是不可以完成赋值结构体变量的内容的,因为结构体变量内存的存储方式多种多样,结构体变量的地址与结构体变量第一个内容的地址相同,但之后的变量内容怎么办?无法通过scanf一次性输入

所以不能使用&s1来实现结构体的输入

14.结构体传参

结构体传参传的是什么?

结构体传参有两种传法,一种是将整个结构体内容全部传过去,另一种则是将结构体变量的地址传过去

示例如下:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>

struct stu  //结构体类型为struct stu
{
	int a;
	char arr[20];
	int d;
}s1 = { 20,{"w"},98 };

void print1(struct stu s1) {
	printf("%d %c %d\n", s1.a, s1.arr[0], s1.d);
    //打印 20 w 98
}

void print2(struct stu* ps) {
	printf("%d %c %d\n", ps->a, ps->arr[0], ps->d);
   //打印 20 w 98
}
int main()
{
	
	print1(s1);
	print2(&s1);
}	

注意:结构体传参时,结构体必须为全局变量,如果为局部变量,结构体传参就会报错,如下图:

cdc71467c76748bda13e385d47bc2ee4.png


问:print1 与 print2 哪个更好些?

答案是首选print2函数,传递结构体指针的地址更好

原因:函数传参时,参数是需要压栈的,会有时间和空间上的开销

如果我们将一整个结构体传过去,结构体的内存越大,参数压栈的系统开销就越大,会导致性能的下降

结论:结构体传参时,首选传递结构体的地址


2a775d9141e049b28a499aa8c5166913.png

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

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

相关文章

IPD推行成功的核心要素(二十二)IPD流程持续优化性地推出具备商业成功潜力的产品与解决方案

产品开发是非常复杂的。随着创新环境的快速发展&#xff0c;大部分研发型企业普遍会面临着这些核心问题&#xff1a;如何快速响应市场的变化&#xff1f;如何推出更有竞争力的产品&#xff1f;如何在竞争中脱颖而出&#xff1f;因此&#xff0c;往往随着企业的规模化发展&#…

深度优先搜索(DFS)在图的运行过程

深度优先搜索(DFS)在图的运行过程 深度优先搜索算法(DFS)C语言实现运行DFS并记录结果节点发现时间和完成时间:图a是一个有向图,为了清晰起见,我们首先假设图a的具体结构如下(节点和边的集合): 图a: 节点集合: {A, B, C, D, E, F} 边集合: {(A, B), (A, C), (B, D),…

带你了解Android Jetpack库中的依赖注入框架:Hilt

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 Hilt概述 Hilt是Google推出的一种用于Android的依赖注入&#xff08;Dependency Injection&#xff0c;DI&#xff09;框架&#xff0c;构建于…

Java 学习中使用文件、网络连接等资源时,未正确关闭资源,导致资源泄漏应该怎么办?

在Java编程中&#xff0c;处理文件、网络连接、数据库连接等资源时&#xff0c;如果没有正确关闭资源&#xff0c;就会发生资源泄漏。资源泄漏会导致系统性能下降、内存占用增加&#xff0c;甚至可能导致程序崩溃&#xff0c;特别是在高负载的系统中。 一、什么是资源泄漏&…

【C+继承】

继承 1.继承的概念及定义2.基类和派生类对象赋值转换3.继承中的作用域4.派生类的默认成员函数5.继承与友元6.继承与静态成员7.复杂的菱形继承及菱形虚拟继承8.继承的总结和反思 1.继承的概念及定义 ->继承的概念 继承的本质&#xff1a;就是继承的父类的成员 ->继承的…

《哈利波特:魔法觉醒》仅16个月欧美停服,引来“阴谋论”

易采游戏网9月11日消息&#xff1a;2022年&#xff0c;当网易宣布将与华纳兄弟合作推出《哈利波特&#xff1a;魔法觉醒》时&#xff0c;全球玩家一片欢腾。不仅在中国市场掀起了一股狂潮&#xff0c;也迅速进入了欧美市场&#xff0c;吸引了无数哈迷和卡牌游戏爱好者。令人始料…

vue+el-table 可输入表格使用上下键进行input框切换

使用上下键进行完工数量这一列的切换 <el-table :data"form.detailList" selection-change"handleChildSelection" ref"bChangeOrderChild" max-height"500"><!-- <el-table-column type"selection" width&quo…

视频融合共享平台LntonAIServer视频分析平台噪声监测优势

LntonAIServer的视频质量诊断功能中的噪声检测是一项关键技术&#xff0c;旨在帮助用户及时发现并解决视频流中的噪声问题&#xff0c;确保视频监控系统的稳定性和可靠性。 在视频监控系统中&#xff0c;噪声问题常常影响到视频画面的清晰度和可用性。噪声可能由多种因素引起&a…

基于SpringBoot+Vue的考研学习分享互助平台

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的考研学习…

仕考网:省考面试流程介绍,提前了解

省考面试流程介绍&#xff0c;一文带大家了解! 一、面试登记及抽签 考生通常需要提前10至30分钟到达指定地点进行登记。 考试工作人员核对考生身份证和面试通知书等相关证件后&#xff0c;进行抽签确定分组和进场顺序。 二、候考阶段 考生完成抽签后进入候考区等待考试。在…

空气开关跳闸的原因及解决办法

空气开关&#xff08;也称为断路器或空气断路器&#xff09;跳闸通常是因为电路中的某些问题导致的。下面是一些常见的原因及解决办法&#xff1a; 1. 过载 原因&#xff1a;当电路中的电流超过空气开关的额定值时&#xff0c;会导致过载保护动作&#xff0c;使空气开关跳闸。…

银行安全用电典型产品解决方案介绍-安科瑞-叶西平

应用背景 银行作为国家重要部门&#xff0c;是国家经济建设的中枢&#xff0c;也是消防的重点单位。用电安全是银行安全保卫工作 的一个重要环节。银行安保设施、数据中心、自助设施、办公设备等能耗单元对用电的连续性、稳定性和安 全性要求非常高&#xff0c;实时监测线路及…

一文理解单点登录与联合登录

img 如今&#xff0c;许多组织都有大量需要每天进行身份验证的应用程序。用户使用传统意义上的凭据在应用程序进行身份验证时容易遭到社会工程学攻击以及信息泄露风险。 单点登录验证&#xff08;Single Sign-On&#xff09; 单点登录身份验证&#xff0c;无论是内部还是外部…

【LabVIEW学习篇 - 24】:生产者/消费者设计模式

文章目录 生产者/消费者设计模式案例&#xff1a;控制LED等亮灭 生产者/消费者设计模式 生产者/消费者是多线程编程中最基本的一种模式&#xff0c;使用非常普遍。从软件角度看&#xff0c;生产者就是数据的提供方&#xff0c;而消费者就是数据的消费处理方&#xff0c;二者之…

基于51单片机的输液滴速吊瓶控制器proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1fbDdoppvtVdg19H3qI9Vzg 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

中国信通院:《大模型落地路线图研究报告(2024年)》(附下载)

前言 近日&#xff0c;中国信息通信研究院&#xff08;简称“中国信通院”&#xff09;发布《大模型落地路线图研究报告&#xff08;2024年&#xff09;》。 本报告重点梳理了大模型应用落地遵循的诊断、建设、应用、管理四个重要阶段&#xff0c;归纳总结出能力分析、需求挖…

手撕Python之正则

1.正则和re模块的联系 正则表达式是一种通用的用来简洁表达一组字符串的表达式&#xff0c;利用正则表达式可以方便快捷的匹配和筛选字符串 举个例子&#xff1a;在一堆数据中进行电话号码的寻找&#xff0c;我们需要根据电话号码的特征在这一堆数据进行电话的寻找&#xff0…

Oracle 12c 及以上版本补丁更新说明及下载方法

参考下面的文章&#xff0c;会对补丁更新的流程有一定的了解。 https://www.modb.pro/db/27255 RU&#xff08;Release Updates&#xff09;是每一个季度的版本升级。包括&#xff1a; 最新的BUG修复最新的安全漏洞补丁最新添加的功能 以19c为例&#xff0c;对应的升级版本升…

太全面了,各种3D​模型和资源都开源了

太全面了&#xff0c;各种3D​模型和资源都开源了 想在三维图形世界中畅游&#xff1f;three-cesium-examples 是一个开源的 three.js 和 cesium.js 案例与演示社区&#xff0c;让你轻松掌握这些酷炫的技术&#xff01;本文将带你了解这个仓库的主要内容、特点&#xff0c;并教…

最通俗的语言搞懂”大模型“的来龙去脉

人工智能时代&#xff0c;有很多时髦、相互容易混淆概念的科技名词&#xff1a;AI、Machine Learning、Deep Learning、Generative AI、Large Model&#xff0c;它们指的是同一个概念么&#xff1f;不是的。 AI&#xff08;artificial intelligence人工智能&#xff09;&#x…