自定义类型详解

news2024/11/17 18:26:09

这次我们来看自定义类型,我们之前接触过的自定义类型就有数组和结构体,我们来详细解析一下这些自定义类型的特点,已经一些我们没接触过的自定义类型

目录

1.结构体

1.1结构体的基础知识

1.2结构体的声明

1.3特殊声明

1.4结构的自引用

1.5结构体变量的定义和初始化

1.6结构体内存对齐

1.7修改默认对齐数 

1.8结构体传参

2.位段

2.1什么是位段

2.2位段的内存分配

2.3 位段的跨平台问题

2.4 位段的应用

3.枚举

3.1枚举类型的定义

3.2枚举的优点

3.3枚举的使用

4.联合(共用体)

4.1 联合类型的定义

4.2联合的特点

4.3联合大小的计算


1.结构体

对于结构体的接触,我们已经有了很多,但是还有很多细节不知道,比如结构体的大小,位段是什么等等,我们来详细讲解

1.1结构体的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
对比数组来看,数组也是一些值的集合,类型是相同的

1.2结构体的声明

struct tag
{
    member-list;
}variable-list;

我们来看结构体的语法形式,struct后跟结构体标签,大括号里是成员列表,最后一行是变量列表,我们来看个例子

我们定义了一个学生结构体stu,学生有姓名和年龄两种成员,s1和s2 是我们创建的结构体变量,是全局变量,而main函数里的s4和s5是局部变量

 

我们之前创建结构体每次都要写struct stu,这样非常麻烦,我们可以使用typedef来对结构体进行重命名,如上图所示,我们给结构体重命名为stu,这样创建变量时就可以直接写stu了,比如s3变量 ,这样创建的s3和s4、s5是没有区别的,typedef的作用就是把复杂的类型简单化

我们这样进行重命名也是可以的 

1.3特殊声明

在声明结构的时候,可以不完全的声明,比如匿名结构体类型

struct
{
	char name[20];
	int age;
}s1;

我们发现这个结构体是没有名字的,s1就是我们的匿名结构体,所以匿名结构体使用时必须在他的后面创建变量,否则他连名字都没有,我们是无法创建变量的,而且匿名结构体的特点是只能使用一次

struct
{
	char name[20];
	int age;
}s1;
struct
{
	char name[20];
	int age;
}*p;
int main() {
	p = &s1;
	return 0;
}

我们看上面这段代码,两个匿名结构体类型是一样的,那我们可以取地址吗?

当我们这样去编译时,编译器是会报警告的

虽然两个结构体的类型看着是相同的,但在编译器来看却是不一样的,一定要记住,匿名结构体只能用一次

1.4结构的自引用

数据结构中有一种结构叫做列表,我们可以把他看作不连续的数组

列表的实现就是使用结构体,结构体里有一个数据,和用来指向下一个节点的元素,那我们这样定义可以吗?

struct Node
{
int data;
struct Node next;
};

答案是错误的,如果我们真的这样定义了,那这个结构体的大小是多少呢?他会无限套娃,是无法计算的,所以这样设计结构体是错误的,我们应该这样设计

struct Node
{
int data;
struct Node* next;
};

用指针来指向下一个节点,此时结构体的大小就是可控的,而这种设计就叫做结构体的自引用,引用自己同类型的其他节点,我们再看一个例子

typedef struct Node
{
int data;
Node* next;
}Node;

我们用重命名来这样定义结构体可以吗?

是错误的,因为结构体定义必须要完善,我们连Node都没有,就不能在结构体里使用Node

1.5结构体变量的定义和初始化

struct stu
{
	char name[20];
	int age;
}s1 = {"zhangsan",20};
typedef struct stu stu;
stu s2 = { "lisi",22 };
int main() {
	stu s3 = {"wangwu",28};
}

对于结构体类型的初始化,我们使用{ }即可,如果是复杂结构体,可以进行嵌套

struct stu
{
	char name[20];
	int age;
}s1 = {"zhangsan",20};
typedef struct stu stu;
struct point {
	int num;
	stu p;
	float x;
};
int main() {
	stu s3 = {"wangwu",28};
	struct point p1 = { 20,{"lisi",20},3.14f };
    printf("%d %s %d %f", p1.num, p1.p.name, p1.p.age, p1.x);
}

比如我们这时的p1,就是结构体嵌套结构体,使用{ }进行嵌套即可,对其进行打印,多次使用 . 操作符就可以了,而且结构体的初始化是可以乱序的

typedef struct s {
	int a;
	char c;
	float x;
	double y;
}s;
int main() {
	s s1 = { .x = 3.14f,.a = 20,.y = 6.66,.c = 'w' };
}

乱序需要指定这次初始化的是哪一个,下次初始化的是哪一个才行

1.6结构体内存对齐

我们上面已经了解了结构体的基本知识,接着我们来讨论一下结构体的大小如何计算

s1和s2的成员变量明明是一样的,只是顺序不同,为什么结构体的大小差异却这么大呢?这就是我们要讲解的内容,结构体的内存对齐

我们首先要了解结构体内存对齐的规则

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

 我们来用上面的s1举例

根据我们的规则,第一个成员从偏移量的0处开始,c1是char类型,占1个字节,如图蓝色所示,从第二个成员开始 ,要对齐到对齐数的整数倍,i是int类型,大小为4,vs默认对齐数为8,取较小值为4,4是4的整数倍,所以从4开始填充,占4个字节,如图绿色所示,c2是char,对齐数从1和8中选较小的为1,8为1的整数倍,所以填充8,所图红色,此时我们已经使用了9个字节,结构体的大小应为结构体所有成员最大对齐数的整数倍,s1的成员最大对齐数为4,所以我们还应该浪费3个空间,加上我们上边浪费的3个空间,如图灰色所示,我们的s1就一共占用了12个字节

我们来证明一下,c语言里有一个宏,offsetof,是用来计算结构体大小偏移量的

再对比我们的图,也是从0,4,8开始填充的,再想想我们计算出该结构体大小为12,每个元素的大小,所以浪费了6字节的空间,使用offsetof需要包含头文件include<stddef.h>

struct s3 {
	double d;
	char c;
	int i;
};
struct s4 {
	char c1;
	struct s3 s;
	double d;
};

我们再来计算一下s3和s4的,s3的比较简单,计算出为大小为16,我们看s4,如果结构体里嵌套了结构体,这个嵌套的结构体,要对齐到自己成员里最大对齐数的整数倍,我们的对齐规则应该加一条,s3成员的最大对齐数为8(double d的对齐数),所以应该对齐到8的整数倍

s3是16字节,所以占16个空间,如图橙色所示,d的对齐数为8,占8个字节,如图紫色所示,此时我们共用了32个字节,是我们最大对齐数8的整数倍,所以s4的大小为32,我们来看看对不对 

那为什么会存在内存对齐呢?

 大部分的参考资料都是如是说的:
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常

2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。

 比如在一个结构体里,有两个成员,一个char,一个int,如果没有内存对齐,在32位机器下,一次会读取4个字节,会出现这种情况

要拿到这个int,我们需要将两次读取内容拼接在一起,而有了内存对齐,就不会出现这种情况 

1.7修改默认对齐数 

我们可以使用#pragma这个预处理指令来修改默认对齐数

#pragma pack(8)

比如我们这样写,就是修改默认对齐数为8,但是vs里的默认对齐数本来就是8,我们修改为1来演示一下

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

1.8结构体传参

结构体传参,也是可以用传值传递和传址传递,函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的
下降,所以,结构体传参的时候,要传结构体的地址。

2.位段

我相信很多人都是第一次见到这个东西,那位段到底是什么呢?我们来详细看看

2.1什么是位段

位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字

举个例子

struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

 这样的A就是一个位段,一个整形(char也属于整形),然后命名,之后加上冒号和数字,这样的格式就是位段,我们先来计算一下他的大小

 为什么会是8呢?明明上边有四个整形,一个整形4个字节,最少也应该是16字节,可结果却和我们想象的不太一样,我们来看位段的内存分配

2.2位段的内存分配

位段的位字,代表着是二进制位,也就是说,上边的_a只占2个二进制位,_b只占5个二进制位,这样的原因是,因为我们在定义某些结构体时,这些结构体的成员取值是非常有限的,比如一个人的年龄,最多也就100来岁,只需要几个比特位就够了,如果给一个整形,一个整形是32个比特位,就有点浪费了,所以我们可以用位段来限制,此时的_a,_b等等不会直接开辟一个整形空间,而是需要多少用多少,这样就可以节省空间,位段和结构体是类似的,结构体可以做什么,位段就可以做什么,只是他比结构体更节省空间而已,但是有好处就有坏处,坏处是可移植性差

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

 我们用上边的位段A来举个例子,计算他的大小

因为我们选择的是int类型,所以会先分配32个bit位,_a需要2个,所以还剩30个,_b需要5个,之后还剩25个,_c需要10个,还剩15个,此时_d需要30个,但是不够了,所以再次开辟一个int的空间,但是这时,_d是先使用剩下的15个,再使用新开辟的,还是直接把剩下的15个浪费掉,这是不知道的,c语言并没有规定,是取决于编译器的,此时我们开辟了两个int的空间,所以是8个字节,另外,_d的大小是30,但是不能是33,后边的数字是不能超过当前类型的,是有范围限制的

我们来看个例子,测试一下vs环境下的位段如何分配内存

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main() {
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

 我们先把这个位段置为0,然后给a赋值10,10的二进制位为1010,但a只能存放3个二进制位,所以只能存放010,b是12,二进制位为1100,一个字节是8个比特位,此时写成2进制为00 00 00 00,我们将a和b存入后变成01100010,最前面的0为谁都没有使用过的,接着存放c,空间不够,开辟新的空间,又是00 00 00 00,c是3,需要5个二进制位,二进制位为00011,所以此时的新空间为00000011,接着我们放d,d需要4个二进制位,但是空间不够,我们再次开辟新的空间,d的二进制序列为0100,放入后为00000100,接着我们把这三个空间连起来

0110 0010 0000 0011 0000 0100,这是我们按照低位向高位使用,而是是直接浪费掉未使用的空间,我们把这串数字转换为16进制为62 03 04,那内存里是不是这样的呢?我们来看一看

和我们计算出的是一样的

 

 并且通过计算,s也确实是占了3个字节

2.3 位段的跨平台问题

1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
 

跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在

2.4 位段的应用


 我们来看个位段在实际应用的例子,位段在网络底层就被使用,比如存在a和b两个人,a向b发了消息,要想b接收到信息,就得对a的信息就行封装,就如上图所示,如果我们给的是固定类型,比如int,char之类,会上边的3位或者13位这种就会不够,或者有浪费,所以可以使用位段进行节省空间,而在网络上节省空间,是有大用处的,我们把网络想象成告诉公路,如果来的全是大车,那么道路就会非常拥挤,而如果都是小车,那就会很畅通,网络的状态也就更好,负载也更小

3.枚举

枚举顾名思义就是一一列举。把可能的取值一一列举。比如一周有7天,我们可以列举出来,性别有男和女,也是可以列举出来的

3.1枚举类型的定义

enum Sex//性别
{
MALE,
FEMALE,
SECRET
};

{ }中的内容是枚举类型的可能取值,也叫枚举常量 

我们可以列举出性别,有男,女,保密三种,就和float,double一样,是一个类型,有了这个类型,我们就可以创建变量

 枚举类型的值是有限的,并且是不可改变的,比如上面的性别,只有这三种,所以叫常量

 

当我们把鼠标指在上边时,会发现它有一个值 ,我们是可以打印出来的

 当然这些是默认值,如果我们感觉不合适,是可以自己替换的

枚举常量,常量最开始也应该有一个值,我们在枚举类型里对他进行赋值,其实是初始化,而不是改变它的值,而我们给定初始值后,就不能改变了

我们再看一些细节

 

 当我们给第一个枚举常量一个初始值后,其他的值会在其后默认递增1

我们还可以从中间开始对其初始化

 

当我们在语言的编译器下对枚举类型赋值我们初始化的值后,好像也没什么问题,但这是错误的, 在c++里,会直接报错,右边的5是整形,而左边是枚举类型,是不匹配的

3.2枚举的优点

我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

 比如对于我们之前的通讯录(大家可以简单看看我之前写过的通讯录)

(3条消息) 简单通讯录的实现_KLZUQ的博客-CSDN博客

 

在我们的switch语句里,如果别人什么都不知道,是很难将1和添加,2和删除这些联系在一起的 

所以我们可以这样写

enum Option {
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};

 这样就很明确了,更容易想到相应的功能

如果我们使用#define来替换FEMALE的话, 只是完成替换,这个FEMALE是没有类型的,在之后代码里遇到FEMALE都是直接替换为5,而不是我们看到的FEMALE,但是使用枚举,是有类型的,更加严谨

而且,枚举类型是可以调试的,是可以观察到的

 而使用#define是直接替换,是看不到的

3.3枚举的使用

只能拿枚举常量给枚举变量赋值,才不会出现类型的差异,另外,我们来看看枚举类型的大小

 

4.联合(共用体)

4.1 联合类型的定义

联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)

以上就是联合体的声明,定义,以及这个联合体的大小,我们来看一些例子

 我们发现这三个地址是一样的

当访问c时,只会访问第一块空间,访问i时会访问这四块空间,当然c和i是不能同时使用的,我们修改i时,c也会被修改

4.2联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员)
我们会发现是至少,而不是确定的,所以联合体的大小其实和我们想的是不一样的,联合体的计算我们下面会讲解,这里,我们先看一道题

判断当前计算机的大小端存储
这道题我们之前用指针解决过,这次我们用联合体来解决它(不清楚大小端和如何判断的朋友可以看我之前的博客)

(3条消息) 数据的存储_KLZUQ的博客-CSDN博客

int main() {
	union Un {
		char c;
		int i;
	}un;
	un.i = 1;
	if (un.c == 1) {
		printf("小端");
	}
	else {
		printf("大端");
	}
}

 

如果大家愿意的话,我们也可以把这段代码封装成一个函数,这里就不再举例 

4.3联合大小的计算

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

联合体也是存在对齐的,和结构体是一样的

 

c是char类型,对齐数为1,编译器默认对齐数为8,取最小为1,n为int类型,对齐数为4,他们两个最大对齐数为4,char c[5],占用5个字节,但不是4的倍数,所有这时会浪费空间到8,为4的整数倍

 以上就是我们自定义类型的全部内容,希望大家可以有所收获

如有错误,还请指正

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

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

相关文章

Mac AndroidStudio生成签名文件,显示SHA1和MD5值

一、首先&#xff0c;生成签名文件 1.点击菜单栏中的Build的。 2.弹出窗体&#xff0c;创建新的KeyStore&#xff1a;如下图。 3.生成jks文件 4.填写keystore和key密码。ketystore密码和key密码在后面会用到。其他信息也不是很重要&#xff0c;自己差不多能记住就好。 5.然后点…

设计模式学习(五):State状态模式

一、什么是State模式 在面向对象编程中&#xff0c;是用类表示对象的。也就是说&#xff0c;程序的设计者需要考虑用类来表示什么东西。类对应的东西可能存在于真实世界中&#xff0c;也可能不存在于真实世界中。 在State模式中&#xff0c;我们用类来表示状态。在现实世界中&a…

【部署】Apache DolphinScheduler 伪集群部署

【部署】Apache DolphinScheduler&#xff08;海豚&#xff09; 伪集群部署&#xff08;Pseudo-Cluster&#xff09;Standalone极速体验版DolphinScheduler 伪集群部署前置准备工作本地部署环境准备 DolphinScheduler 启动环境配置用户免密及权限配置机器SSH免密登陆启动zookee…

解决bootstrap table footerFormatter表脚和表体列错位问题

需求&#xff1a;需要把表格的一列的内容在表最下面一行进行汇总&#xff0c;显示合计 实现合计步骤&#xff1a; 1.在初始化表格时&#xff0c;开启显示表脚的属性&#xff0c;如图 2.在需要合计的列中添加footerFormatter函数&#xff0c;如图 这样就会在表格最下面显示合计…

年度征文 | 再见2022,你好2023

年度征文 | 再见2022&#xff0c;你好2023题记个人简介初衷写作展望题记 这两天打开CSDN&#xff0c;发现消息栏一直提示我写这个年度征文&#xff0c;本来觉得自己没有经验可以分享&#xff0c;毕竟自己仍处于该领域的起步阶段&#xff0c;无法给大家提供有用的建议或实用的总…

功率放大器模块是什么意思(功率放大模块工作原理)

在日常电子实验测试中&#xff0c;很多电子工程师都会经常使用到功率放大器&#xff0c;随着人们对于功率放大器的频繁使用&#xff0c;对于功放的要求也越来越多&#xff0c;有些工程师就想要使用尺寸较小的仪器&#xff0c;功率放大模块便应运而生&#xff0c;今天就请安泰电…

关于几个坐标系的关系NED ENU ROS

几个坐标系转来转去&#xff0c;时间一长又搞混了。 地球固连坐标系 WND 地球固连坐标系 NED 机体坐标系 NED 惯性系 x轴在多旋翼对称平面内指向机头&#xff08;机头方向与多旋字形或X字形相关&#xff09;。 z轴在飞机对称平面内&#xff0c;垂直轴向下。然后&#xff0c;按…

从GPT到chatGPT(二):GPT2

GPT2 文章目录GPT2前言正文摘要方法概述训练数据输入表示模型结构实验语言模型Children’s Book Test&#xff08;CBT&#xff09;LAMBADAWinograd Schema Challenge&#xff08;WSC&#xff09;Reading ComprehensionSummarizationTranslationQuestion AnsweringGeneralizatio…

Linux下搭建Git服务器

目录 步骤一.安装Git&#xff1a; 步骤二.安装Gitosis 安装Gitosis依赖的工具 进入到Gitosis目录执行&#xff1a; 出现下面的信息表示安装成功 步骤三.服务器端创建git用户来管理Git服务 服务器端的Git配置公钥 步骤四.服务器端创建Git仓库 步骤五.客户端clone服务器…

PPT如何转换成PDF?三种转换方法告诉你

大家在工作中会使用PPT格式的文件来汇报工作流程吗&#xff1f;当上级领导要求你将这些工作内容全部汇总&#xff0c;并用PDF文件发给他的时候&#xff0c;你是怎么做的呢&#xff1f;是重新将内容复制黏贴到PDF文件中吗&#xff1f;今天告诉你一个简单的方法&#xff0c;其实只…

【echarts】自定义legend样式 echarts图例与⽂字对齐问题

较完整的使用介绍参考&#xff1a;https://blog.csdn.net/changyana/article/details/126281275 起因 今天使⽤echarts时发现官⽹⽰例图例部分并没有进⾏对齐&#xff0c;⼀上⼀下逼死强迫。。。 解决办法 textStyle: { // 添加height: 10, // 关键在这个height设置rich: …

前端开发-异常问题记录

Q1&#xff1a;Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示&#xff1b; 可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决 Q2&#xff1a;浏览器跨域问题&#xff08;通过配置浏览器解决&#xff09; 将chrome浏览器复制一个进行修改&#xff…

CRM如何帮助企业提高工作效率?

企业管理者在创建团队时&#xff0c;除了要凝聚人心&#xff0c;更注重效率&#xff0c;企业团队的工作效率直接决定了企业的生产力&#xff0c;生产力决定战斗力&#xff0c;使用CRM系统可以帮助企业提高工作效率。 前言 企业管理者在创建企业团队时&#xff0c;除了要凝聚人…

IOS开发基础 · SwiftUI · StanfordCS193p Lecture3-4

IOS开发Lecture3MVVMVarieties of Typesstruct & classdont care - genericsFunctionClosuresprivate(set)for函数作为参数传给函数初始化顺序Lecture4修改代码View界面预览代码修改构建View-ViewMode点击事件让bool值反转internal external nameprint("\( )")st…

NeurIPS 2022: S3GC 可扩展图聚类

talk 文章的创新性不大&#xff0c;meta-review是给了 如果还可接收&#xff0c;再考虑。 但是 本文确实是 可扩展图聚类的重要一步。已有的方法 或多或少只在 小的数据集上 进行聚类。 存在一些非聚类的通用gnn方法进行采样。本文就是利用采样降低复杂度&#xff0c;并进行大…

基于FPGA的UDP 通信(四)

引言 前文链接&#xff1a; 基于FPGA的UDP 通信&#xff08;一&#xff09; 基于FPGA的UDP 通信&#xff08;二&#xff09; 基于FPGA的UDP 通信&#xff08;三&#xff09; 本文基于FPGA与MATLAB作千兆以太网通信模块UDP数据接收实验板级验证&#xff1a;FPGA接收上位机数…

大前端 TOB 0.5 WordPress模板 漂亮大气自适应多终端多功能

tob主题基于WordPress程序&#xff0c;响应式布局支持电脑、平板和手机的完美展示。tob适用于各种图片展示网站、新闻站、电影站、美图站、资源站等等&#xff0c;扁平化设计、公众号展示、打赏功能、列表无限加载、相册功能。tob是基于WordPress程序的主题&#xff0c;由theme…

多线程与高并发(五)

【ReentrantLock源码】&#xff1a; 【AQS源码】&#xff1a; 【公平与非公平】&#xff1a; 【公平】&#xff1a; 线程想要获得一把锁&#xff0c;乖乖的去这把锁的等待队列里排队————公平。 【非公平】&#xff1a; 线程想要获得一把锁&#xff0c;不去排队&#xff0c…

嵌入式实时操作系统的设计与开发(九)

同步机制 aCoral信号量机制不仅可以实现临界资源互斥访问&#xff0c;控制系统中临界资源多个实例的使用&#xff0c;还可以用于维护线程之间、线程和中断之间的同步。 当信号量用来实现同步时&#xff0c;起始值为0&#xff0c;如一个线程正在等待某个I/O操作&#xff0c;当…

【外贸小知识】通过whatsapp获取流量的几种小方法

相信做外贸的小伙伴们对于是whatsap比较熟悉的&#xff0c;都想通过whatsapp来获取更多流量&#xff0c;更多用户。今天我们花漾灵动小编就给大家汇总了通过whatsapp获取流量的几种小方法&#xff0c;希望能对新手小白有点作用哦&#xff01;通过whatsapp获取流量的几种小方法1…