【C语言笔记】自定义类型全解
【C语言笔记】自定义类型全解
- 一、结构体
- 1、什么是结构体
- 2、结构体的声明与定义
- 2.1、结构体的声明
- 2.2、对结构体成员的访问
- 2.3、对结构体成员的初始化
- 2.4、结构体的不完整声明
- 2.5、结构体嵌套定义
- 3、结构体的自引用
- 3.1、错误的自引用
- 3.2、正确的自引用
- 4、结构体大小的计算
- 4.1、结构体的内存对齐
- 4.2、柔型数组
- 5、结构体实现位段
- 5.1、什么是位段
- 5.2、位段的注意事项
- 5.3、位段的内存分配
- 二、枚举
- 1、枚举类型的定义
- 2、枚举的优点
- 三、联合体
- 1、联合体类型的声明与定义
- 2、联合体大小的计算
一、结构体
1、什么是结构体
在C语言中有一种数据类型称为“聚合数据类型”,它能同时存储一个或一个以上的单独数据,C中提供了两种类型的聚合数据类型,分别是数组和结构体。
我们知道数组一组相同类型的元素的集合,但结构体中的元素类型是可以不相同的。
所以数组可以用下标的方式进行反问(因为它每个元素类型相同),而结构体就不能用下标进行访问。
2、结构体的声明与定义
2.1、结构体的声明
结构体的声明形式如下所示:
其中tag表示的是结构体的标签名,即这个结构体叫什么名字,member-list是结构体成员列表,它包含了一个或一个以上的结构体成员,而variable-list是结构体变量列表,它列出了在创建结构体时同时创建的此类型的变量。比如我们现在可以定义一个学生类型的结构体:
这里的student就是tag,二大括号内部的id、age、name就是这个结构体的成员变量列表。后面的s1、s2就是结构体变量列表,而且像s1和s2这样的在结构体声明的时候就创建的变量都是全局变量。
在声明结构体时,必须列出它所包含的所有成员。这个列表包括每个成员的类型和名字。
当然这个variable-list也并不是必须要有的,甚至这个tag也是可以没有的。
这些后面都会讲到。
但最后那个结尾的分号;是千万不能省略掉的
2.2、对结构体成员的访问
对结构体成员的访问在之前将操作符的时候已经遇到过了,但今天既然已经讲到了结构体,那我们还是再来复习一下吧。
对结构体成员的访问方试有两种,分别是直接访问(.)和间接访问(->),点是对结构体变量使用的,所以称为直接访问箭头是对结构体指针使用的,所以称为间接访问。
比如我们可以再主函数创建了一个学生类型之后,直接用点的方式打印其成员变量:
也可以通过一个结构体指针指向s后通过指针来进行打印:
总之对结构体成员的访问方试有两种,具体使用哪一种还是要看场景。
2.3、对结构体成员的初始化
对结构体成员的初始化其实上面就已经用到了,和数组一样,也是用一个大括号括起来,里面再写成员变量的值,当然,我们也可以像数组一样对结构体成员进行不完全初始化,即大括号里只写一个0:
通过调试窗口我们可以看到,这里不仅是id被置成了0,连后面的age也默认置成了0,二名字也被置成了空字符串。
当然了,如果我们不想按顺序初始化也可以,比如说我就想把名字写在前面:
这时候就要在成员变量前面加上一个点。当然这样也是可以不完全初始化的,不完全初始化的情况下,其他未被赋值的成员默认为0。
2.4、结构体的不完整声明
在声明结构体的时候,也可以不完全声明,比如我们可以把结构体标签明给省略掉:
struct {int id;int age;int name[10];char sex[4];
} s1 ;
但这样的声明方式的缺陷是你只能像上面一样,在创建的时候就定义好全局变量,以后你在想用这个结构体来定义变量是不可能的啦,因为它连名字都没有,没人认识它,也没有人能找到它。
所以,匿名声明的结构体的使用都是一次性的。
2.5、结构体嵌套定义
结构体也可以嵌套定义,即在一个结构体中定义另一个结构体,例如:
struct Student {int id;int age;struct Name {char first_name[15];char last_name[15];};struct Name name;char sex[4];
};
当一个类型A只会在另一个类型B中被使用的时候,就可以吧类型A定义在类型B的定义体内,这样可以减少暴露在外面的用户自定义类型的个数。
3、结构体的自引用
其实结构体也可以包含一个结构体本身类型的成员的,就比如说各位的身怀绝技的老师一定也是另一个更身怀绝技的老师教出来的,所以在一个老师的结构体的成员变量中也可以含有一个老师类型的变量。
那我们应该怎样实现结构体的自引用呢?
3.1、错误的自引用
struct Teacher {int id;int age;int name[20];struct Teacher teacher;
};
上面的这种自引用方式有什么问题吗?
好像咋一看确实没有什么问题,但若是我问你这结构体的大小是多大呢?(我们这里先不管结构体大小的计算方法),那一定要包括所有变量的大小啦,但是这个teacher的大小又该怎么算呢?难道又是继续向里面算,又包括了id、age、name 而且还有一个teacher的大小吗?
这样算岂不是永无止尽吗。这难道真像人们所说“学无止境”了吗?
很显然,这种自引用方式是错误的。
正确的饮用方式应该像下面这样:
3.2、正确的自引用
struct Teacher {int id;int age;int name[20];struct Teacher *Te;
};
我们其实只需要通过老师找到老师的老师即可,所以我们只需要存储老师的老师的地址即可,这样我们在结构体中存储的就只是个地址,就并不会出现向上面一种方式那样的无限循环的情况了。
4、结构体大小的计算
这一块知识算是结构体中比较重要的知识了,也是相对来说比较难的知识。
比如我们现在有一个这样的结构体:
struct A {char a;int b;double d;
};
我问你,sizeof(struct A)等于多少,或者A变量所占的内存空间是多大?
你能立刻回答我吗?
是不是a的1字节加上b的4字节再加上d的8字节等于13字节呢?
但最后如果我们在屏幕上打印出它的大小,就会发现并不是13,而是16:
这到底是怎么一个原理呢?
4.1、结构体的内存对齐
想要搞清楚结构体是怎样计算大小的,就一定要先搞清楚结构体在内存中是怎样“对齐”的。
其实结构体成员变量在内存空间中的存储并非是像我们想当然那样一个紧挨着一个存放的。结构体成员变量在存储的时候是按照一定的规则存储的,这些规则总结起来就以下四条:
- 第一个成员在结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = “编译器默认的一个对齐数 与 该成员大小的较小值”。- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
这咋一看好像信息量有点儿大啊,但不没关系,我来带着大家一条一条来看一条一条地来解释,就拿上面的例子来举例:
struct A {char a;int b;double d;
};
第一条规则的意思是不管你第一个成员变量的类型是什么,占用几个字节,都是从地址偏移量为0(也就是起始位置)的位置开始存放的。
第二条规则说的是,当你从第二个成员变量开始你就要考虑“对齐数”了,你的变量要放到“对齐数”的整数倍的地址偏移量处,该对齐数为**“编译器默认对齐数和该成员变量大小的较小值”** 。
我这里使用的编译器是vs,vs的默认对齐数为8,二这里的变量b的类型是int,4字节,所以对齐数自然就是4。所以我们在偏移量为4的地址开始存放b。
有了以上的规则,我们就可以一直存储我们的变量了,当我们所有的成员变量都按照以上规则存储完了之后:
是不是就完事了呢?我们的结构体是不是就是16字节了呢?
其实还没有,这时候就要用到我们的第3条规则了,通过规则2,我们可以算出这三个变量a、b、d的对齐数分别是:1、4、8,所以我们的最大对齐数是8,我们看看现在的结构体大小为16字节,正好是8的整数倍。所以我们的结构体A的大小就是16字节。
但要是我们活生生的在A中再加入一个成员变量e,那结果就大不同了:
当我们存完所有的成员变量之后,发现结构体的大小为20个字节,并不是最大对齐数8的整数倍,所以这时要多开辟出4个字节的空间,凑成24个字节才够8的整数倍。
但当我们结构体中又包含了其他结构体时呢?其大小又该怎么计算呢?
这时候就要用到第4条规则了:
例如我们现在有这两个结构体:
struct C {char a;int b;char c;
};struct D {int e;short s;struct C c1;int f;
};
通过上面的方法我们可以很容易的算出,C的大小为12字节:
但这个C放到D里面又是怎么放的呢?其实这里我们可以把C看成是一个整体(例如一个很大的变量)而它的对齐数就是其自身的成员变量中最大对齐数中的最大值。我们这里很容易得知C的对齐数就是4,所以我们就可以把C放到D中了:
我们可以计算得知,此时D所占的空间大小为24字节,而我们结构体D整体的大小是所有对对齐数(含嵌套结构体)的整数倍是否满足呢?
我们可以很容易得知,此时的整体最大对齐数其实就是4,所以是满足的。所以结构体D的大小就是24字节。
4.2、柔型数组
柔型数组这个概念可能你连听都没有听说过,但它确确实实是存在的,柔型数组指的是结构体中元素允许是未知大小的数组,它必须是结构体中的最后一个成员,且结构体中的柔性数组前面至少要有一个成员。
例如我们在之前的结构体C中再次增加一个数组成员:
struct C {char a;int b;char c;int arr[];
};
这里增加的arr就是一个柔性数组。
我们知道C的大小为12个字节,那增加了这么一个数组对它的大小有什么影响吗?
我们可以来看看:
我们可以看到,好像确实没有什么影响。
确实,sizeof返回的这种结构大小是不包括柔性数组的内存的。
还有一点就是包括柔性数组成员的结构体需要用malloc函数对其进行内存的动态分配,并且所分配的内存应该大于结构体的大小,以适应柔性数组的预期大小,例如我们可以为上面的结构体C开辟一块20字节的内存空间:
struct C* s1 = (struct C*)malloc(20 * sizeof(char));
有人可能会好奇了,这里给它开辟了20个字节的空间,那它的大小是不是就是20字节了呢?
我们可以验证一下:
我们会惊奇的发现,这里*s1的大小还是12字节,连一个字节都没有增加,这也恰好说明了结构体的大小其实不包括柔性数组在内。
5、结构体实现位段
5.1、什么是位段
位段的声明与结构体类似,但它的成员是一个或多个位的字段。这些不同长度的字段实际存储于一个或多个整型变量中。
一个位段的声明形式如下:
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
位段的声明和任何普通的结构体成员声明相同,出了两点以外:
1.位段的成员必须是 int unsigned int signed int 或者是 char (属于整形家族)类型。
2.在成员后面是一个冒号和一个整数。
位段成员冒号后面的整数表示的是该成员一共占用的字节数,也就是该成员最大可以占用的字节数,这就起到了一个限制成员大小和节省空间的作用。
5.2、位段的注意事项
位段的使用其实就是为了节省空间,限制数据的大小,但是位段的不确定性因素太多。所以注重可移植性的程序应该避免使用位段。
位段的不确定性因素大概有以下几点:
1.int位段应该被当做有符号数还是无符号数不确定。
2.位段中的成员在内存中是从左向右分配还是从右向左分配不确定。
3.当一个声明制定了两个或两个以上的位段,且存在后一个位段比较大,无法容纳于前一个位段的剩余位时,编译器到底是把后一个位段存放到下一个字节,还是直接从前一个位段的剩余部分开始放,从而在两个内存位置边界上形成重叠,这也是不确定的。
5.3、位段的内存分配
由于位段的不确定性因素太多,这里首先进行说明这里演示的环境是:
小端机器、int是有符号int、位段成员在内存中是从右向左分配的、这里编译器是把较大的位段放到后一个字节的。
比如说我们现在有这样一个位段:
struct A
{char _a : 2;char _b : 5;char _c : 7;char _d : 6;
};int main() {struct A a = { 0 };// 我们给a中的成员赋一些值a._a = 5;a._b = 10;a._c = 7;a._d = 20;return 0;
}
这里遇到了第一个成员_a,_a的类型是char,所以就会首先开辟一个字节的空间,但_a只占两个比特的位置所以在赋值5给它的时候就会发生截断,实际放的是01。
这里遇到的第二个成员_b只占5个比特,之前开辟出的1个字节的空间是足以存放它的,所以就不用开辟新的空间,因为10的二进制序列才4比特位,所以_b是完全容得下的,所以实际存储的就是10本身。
当遇到第3个成员_c时,剩余的一个比特位已不足以容纳_c了,所以要新开辟一个字节的空间,而_c的7个字节是足以存放下7这个整型的,所以_c里存放的就是7。
后面的操作也是和前面的一样了,当我们放完所有的成员之后,我们可以看到A的大小应该是3字节,而A中的序列应该是像下图这样:
我们可以到内存中去检验一下:
我们发现sizeof(struct A)确实是3,而且内存中的前三个字节也确实是29 07 14。
所以对于位段的内存分配方式我们也清楚了。
二、枚举
在现实生活中总有一些东西是能够一一列举出来的,比如人的性别就男女两种,一周就只有7天星期一到星期天。
这对应到C语言中就是枚举类型了。
1、枚举类型的定义
枚举类型的定义形式就像下面这样:
enum Day
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
上面的enum Day就是一个枚举类型,大括号{}中的内容就是枚举类型可能的取值,也成为了枚举常量。
而这些枚举常量也都是有值的,在未进行初始化的情况下,它们的值从第一个常量开始默认为0,往下以1递增。
如果我们现在打印这些常量的值,就可以看到:
它们默认是0到6。
如果我们对中间一个赋初始值,的话就会看到:
也就是说,枚举中的常量如果不初始化的话,都是向下递增的,只是初始化后就会按照初始化的值为基准向下递增。
2、枚举的优点
枚举的优点可以总结为以下5点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
第一点增加代码的可读性和可维护性其实就是使代码读起来更直观,不用老是前后对照,比如我们想实现一个多功能的计算器,可以执行整数的加减乘除运算,那我们必定就要设计一个方案来根据输入判断你要执行的操作,我们可能会这样设计:
void menu() {printf("***************0、exit***************\n");printf("*******1、Add 2、Sub********\n");printf("*******3、Mult 4、Div********\n");printf("*************************************\n");
}int main() {int input = 0;do {menu();printf("请选择:");scanf("%d", &input);switch (input) {case 1:// 执行加法运算break;case 2:// 执行减法运算break;case 3:// 执行乘法运算break;case 4:// 执行除法运算break;case 0://退出程序break;default:printf("输入有误,请重新输入\n");break;}} while (input);return 0;
}
这样确实没问题,但如果我们有时候忘记了那个数字对应的是哪一种运算,好像又要去查一查菜单,这样好像有点儿麻烦。
这时候我们就可以用枚举来帮我们来解决这个麻烦了:
enum Option {Exit,Add,Sub,Mult,Div
};
这样我们就可以将我们的选项全都换成枚举了:
int main() {int input = 0;do {menu();printf("请选择:");scanf("%d", &input);switch (input) {case Add:// 执行加法运算break;case Sub:// 执行减法运算break;case Mult:// 执行乘法运算break;case Div:// 执行除法运算break;case Exit://退出程序break;default:printf("输入有误,请重新输入\n");break;}} while (input);return 0;
}
这样不就直观多了吗?
我们知道 #define定义的标识符做的工作只是“替换”,而非定义一个类型,所以 #define定义的标识符是没有类型可言的。但枚举是有类型的,这就意味着我们要想把非枚举类型的数据赋值给枚举类型是非法的。所以枚举更严格。
枚举定义的常量只能在枚举类型里使用,而 #define定义的符号是全局的,任何地方都可以使用,也就是说define定义了一个符号后,其他地方就不能出现与它同名的符号了,这就叫做命名污染。而枚举定义的常量就不存在这种问题,其他地方也可以有与枚举常量同名的符号。
而易于调试是相对于define定义的符号来说的,define定义的符号所做的工作是“替换”,而“替换”后我们是看不见define定义的那个符号的,比如:
所以在调试的时候,我们并不知道那个变量可能会是用X这个标识符算出来的,也就不便于我们调试。枚举是有类型的,它在调试中是可以清楚地看到其类型的:
三、联合体
联合体也是一种特殊自定义类型,它与结构体非常类似。但和结构体不同的是,联合体的所有成员引用的都是内存中的相同位置。
1、联合体类型的声明与定义
联合体的声明形式如下:
union Un
{char c;int i;
};
定义联合体的形式如下:
union Un un;
2、联合体大小的计算
联合体的特点就是所有成员共用一块空间,这就是的联合体的大小至少是联合体中最大的成员的大小,例如我们现在有下面这样的一个联合体:
union Un {char c;int i;
};
我们可以打印出它的大小来看看:
就会发现果然是4。
而且联合体内的成员在同一时间里,只能有一个相同的值。例如我们创建一个联合体a,将a.i赋值成1,然后打印a.c就会发现,a.c也是1:
当你有两个变量,而且每次时使用都只可能用到其中的某一个时,就可以考虑将这两个变量放到联合体中,所以联合体的存在事实上也是为了节省空间的。
相关文章:

【C语言笔记】自定义类型全解
【C语言笔记】自定义类型全解一、结构体1、什么是结构体2、结构体的声明与定义2.1、结构体的声明2.2、对结构体成员的访问2.3、对结构体成员的初始化2.4、结构体的不完整声明2.5、结构体嵌套定义3、结构体的自引用3.1、错误的自引用3.2、正确的自引用4、结构体大小的计算4.1、结…...
文心一言硬刚ChatGPT。文心一言能否为百度止颓?中国版ChatGPT“狂飙”的机会在哪儿?
一.文心一言介绍 今天,3月16日消息,百度于北京总部召开新闻发布会,主题围绕新一代大语言模型、生成式AI产品文心一言。百度首席技术官王海峰现在详解了文心一言背后的文心大模型及技术特性。 文心一言是新一代知识增强大语言模型࿰…...

【RabbitMQ笔记10】消息队列RabbitMQ之死信队列的介绍
这篇文章,主要介绍消息队列RabbitMQ之死信队列。 目录 一、RabbitMQ死信队列 1.1、什么是死信队列 1.2、设置过期时间TTL 1.3、配置死信交换机和死信队列(代码配置) (1)设置队列过期时间 (2ÿ…...

Python04 数据序列-字符串
Python04 数据序列-字符串 4.1 字符串概念 字符串是 Python 中最常用的数据类型。我们可以使用引号( ’ 或 " )来创建字符串。 格式: 变量名 数据 / "数据" / """ 数据 """案例: a hello world b &q…...

Redis限流接口防刷
Redis限流接口防刷 Redis 除了做缓存,还能干很多很多事情:分布式锁、限流、处理请求接口幂等性。。。太多太多了~ 大家好,我是llp,许久没有写博客了,今天就针对Redis实现接口限流做个记录。废话不多说&am…...

Yarn 资源调度器
Yarn 资源调度器:资源调度平台,负责为运算程序提供服务器运算资源 1 Yarn 基础架构 YARN 主要由 ResourceManager、NodeManager、ApplicationMaster 和 Container 等组件构成。 MR 程序提交到客户端所在的节点。YarnRunner 向 ResourceManager 申请一个…...

通达信 34日上升三角形主图源码
请先看效果图。 以下是编程源码,可以参考学习一下: N:34;{三角背景} 趋势下:DRAWLINE(HHHV(H,N),H,LLLV(L,N),L,0),LINETHICK2,COLORMAGENTA; SX:REF(趋势下,1)<趋势下; SS:DRAWLINE(SX,趋势下,REF(SX,1),REF(趋势下,1),1); DRAWBAND(SS,RGB(0,0,16…...
CSDN周赛第37期题解(Python版)
这期周赛题目和测试集还算完整,没有出现往期的bug。1、题目名称:幼稚班作业幼稚园终于又有新的作业了。 老师安排同学用发给同学的4根木棒拼接成一个三角形。 当然按照正常的逻辑,如果不能拼接成三角形。 必然要折断某个木棍来拼接三角形。 可…...
程序调试方法
调试思路 程序中一定要尽可能的做容错处理,可能会出错的地方,增加打印日志,这样在出问题时候才能最快的定位问题,所以这个属于前置工作,前置做的越多越好,后期调试越省力,程序也更健壮。学会看…...

【Android入门到项目实战--2.3】—— 活动的四种启动模式(standard、singleTop、singleTask、singleInstance)
目录 一、活动的启动模式 1、standard 2、singleTop 3、singleTask 4、singleInstance 本篇文章主要讲解活动的生命周期和活动的启动模式。 一、活动的启动模式 活动的启动模式共有4种,分别是standard、singleTop、singleTask和singleInstance; 可…...

SpringCloud微服务技术栈.黑马跟学(三)
SpringCloud微服务技术栈.黑马跟学 三今日目标1.初识Docker1.1.什么是Docker1.1.1.应用部署的环境问题1.1.2.Docker解决依赖兼容问题1.1.3.Docker解决操作系统环境差异1.1.4.小结1.2.Docker和虚拟机的区别1.3.Docker架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker架构1.3.4.…...

学习Java——集合类
目录 1.Collection和Collections区别 2.Set和List区别 3.ArrayList和LinkedList和Vector的区别 4.Set如何保证元素不重复 5.Arrays.asList获得的List使用时需要注意什么 1.Collection和Collections区别 Collection 是一个集合接口。 它提供了对集合对象进行基本操作的通用…...

[前端笔记035]vue2之脚手架vue-cli
前言 本笔记参考视频,尚硅谷:BV1Zy4y1K7SH p61 - p95 简介 Vue 脚手架是 Vue 官方提供的标准化开发工具,vue-cli使用步骤 如果下载缓慢请配置 npm 淘宝镜像:npm config set registry http://registry.npm.taobao.org全局安装vue/cli&#…...

《Linux的权限》
本文主要对linux的一些基本权限进行讲解 文章目录前言Linux权限(1)权限的概念(2)linux下用户分类(root,普通)(3)linux的文件属性文件属性的分类文件权限修改文件权限1、chmod2、chown和chgrp3、fiile权限的三个重要的问题第一个问…...
js类型转换
类型转换 1.字符串转换 字符串转换在原来值的基础上加上 "" let num 1 num String(num) // "1"String(false) // "false"2.数字转换 在算数函数和表达式中,会自动进行数字转换。其自动完成的数字转换为隐式转换,也可…...

PostMan工具的使用
PostMan工具的使用 1 PostMan简介 代码编写完后,我们要想测试,只需要打开浏览器直接输入地址发送请求即可。发送的是GET请求可以直接使用浏览器,但是如果要发送的是POST请求呢? 如果要求发送的是post请求,我们就得准备页面在页…...

Sentinel 授权规则规则持久化
本篇博客我们来学习授权规则,授权规则是对请求者的一种身份的判断。 1、授权规则 授权规则是对请求者的身份做一个判断。你有没有权限来访问我?那就有人可能会说这个功能,好像以前我们在学习微服务的时候讲过网关他不就是把门的吗࿱…...

C#大型HIS医院LIS管理系统源码
▶ 一、实验室信息管理系统(LIS)是什么? 实验室信息管理系统也就是平时所说的LIS(Laboratory Information System)系统,其主要服务的对象主要是医院检验科工作人员,也是医院信息化建设必…...

Java基础学习(5)
Java基础学习一 面向对象1.1 介绍对象1.2 设计对象并使用1.2.1定义类的补充注意事项1.3 封装好处:1.3 private关键字1.4 this关键字1.5 构造方法构造方法的注意事项:1.6 标准的JavaBean1.7 对象内存图1.7.1 一个对象的内存图1.7.2 两个对象内存图1.7.两个引用指向同一个对象1.8…...

SpringBoot接口 - 如何生成接口文档之Swagger技术栈
SpringBoot开发Restful接口,有什么API规范吗?如何快速生成API文档呢?Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上&am…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...