自定义类型:结构体类型

在学习完指针相关的知识后将进入到c语言中又一大重点——自定义类型,在之前学习操作符以及指针时我们对自定义类型中的结构体类型有了初步的了解,学习了结构体类型的创建以及如何创建结构体变量,还有结构体成员操作符的使用,现在我们将继续结构体相关知识的学习,希望在在看完本篇后你将会有新的收获,一起加油吧!!!


 1. 结构体类型的声明

1.1结构体回顾

在之前c语言常用操作符(2)中已经对结构体的声明进行了讲解,那么现在我们简单的复习一下

struct tag//tag的名字可是自定义的
{
member-list;//成员列表
}variable-list;//变量列表

例如创建一个描述学生的结构体

struct Stu//学生类型
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢

对结构体进行声明后就可以创建结构体变量并进行初始化了

struct Stu//学生类型
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
 
 
int main()
{
//按照结构体成员的顺序初始化
struct Stu s = { "张三", 20, "男", "20230818001" };
printf("name: %s\n", s.name);
printf("age : %d\n", s.age);
printf("sex : %s\n", s.sex);
printf("id  : %s\n", s.id);
//按照指定的顺序初始化
struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex =
"⼥" };
printf("name: %s\n", s2.name);
printf("age : %d\n", s2.age);
printf("sex : %s\n", s2.sex);
printf("id  : %s\n", s2.id);
return 0;
}

1.2结构体的特殊声明

在结构体中如果我们在创建结构体类型时不给结构体加上名字,那么这个结构体就是匿名结构体类型,例如以下代码就两个都是是匿名结构体类型

struct 
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}s1,s2;

struct 
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}*p1,*p2;

int main()
{
 p1=&s1;

return 0;
}

在以上代码中你认为s1=*p1这种写法是合法的吗?
在以上代码中先是创建了一个匿名结构体类型并创建了两个变量,之后又创建了一个结构体类型的指针,后将结构体变量s1的地址传给指针p1,
这时就会存在问题了原因是但我们创建两个结构体类型,虽然这两个结构体类型的成员都相同,但当创建两个匿名结构体类型时,编译器会默认这两个结构体的类型不相同,所以这时将s1的地址传给指针p1会使得等号两边的变量类型不相同 

注:匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用⼀次,
       所以使用匿名结构体类型时基本上是只打算对该结构体类型使用一次,之后不再使用

1.3结构体的自引用

数据在内存当中可能是连续存放的,也可能是像以下这样随机存放的

把1,2,3,4,5叫做节点,在节点中除了要存放数据外还要能有下一个节点的信息,这时我们就可以认为一个节点是一个结构体,那么各结构体能写成以下形式吗?

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

 这时将结构体里面的一个成员为相同的结构体类型,这时就存在一个问题,当我们用sizeof求struct Node的大小,那么使得大小为无穷大,所以以上这种写法是错误的

每各节点中我们是要存放下一个节点的信息,所以在节点中存放下一个结构体的地址也是符合要求的,因此就可以在结构体里面存放一个结构体指针类型,这时两个结构体类型不同就不会存在计算struct Node结构体时候出现无穷大的问题了

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

 我们知道使用typedef可以将变量类型简单化,如果在以下代码中将struct Node重命名为Node,那么结构体内的成员也可以简化为Node* next吗?

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

答案是不行的,原因是在对struct Node重命名前就使用简化后的名称Node会使得Node* next是未定义的,这时程序就会报错 

修改以下形式就是正确的了

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

2.结构体的内存对齐

在想要学习计算结构体在内存当中的大小,就需要学习一个重要的知识点:结构体的内存对齐

2.1内存对齐规则

#include<stdio.h>
struct s1
{
	char c1;
	char c2;
	int n;
};

struct s2
{
	char c1;
	int n;
	char c2;
};
int main()
{
	printf("%zd", sizeof(struct s1));
	printf("%zd", sizeof(struct s2));
	return 0;
}

以上两个结构体的大小相同吗? 
运行程序发现结果不一样,这是为什么呢?

结构体的对齐规则:
1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员变量大小的较小值

- VS 中默认的值为 8
- Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小
3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的
整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构
体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

要理解以上的对齐规则首先要了解偏移量偏移的是什么
其实偏移量就是相较于起始位置的字节偏移量


如果我们想知道s1中的各个成员变量的偏移量是多少,这时就可以用到一个宏offsetof,是c语言提供给我们的宏,作用是计算结构体成员相较结构体变量起始位置的偏移量

这时我们打开c++官网查看offsetof




 

注:使用offsetof需要引用头文件#include<stddef.h> 

#include<stdio.h>
#include<stddef.h>
struct s1
{
	char c1;
	char c2;
	int n;
};


int main()
{
	printf("%zd\n", offsetof(struct s1,c1));
	printf("%zd\n", offsetof(struct s1,c2));
	printf("%zd\n", offsetof(struct s1, n));
	return 0;
}

​ 

 这时运行程序就可以得到c1,c2,n相较起始位置的偏移量分别为0,1,4

这时就会发现与以上计算出的struct s1的大小8字节相同
 

#include<stdio.h>
#include<stddef.h>
struct s1
{
	char c1;
	char c2;
	int n;
};
struct s2
{
	char c1;
	int n;
	char c2;
	
};

int main()
{
	printf("%zd\n", offsetof(struct s2,c1));
	printf("%zd\n", offsetof(struct s2,c2));
	printf("%zd\n", offsetof(struct s2, n));
	return 0;
}


 

这时就会发现这样计算出的struct s2大小为9个字节,这就与以上运行程序计算出的12不相同了,这是为什么呢? 

这时我们就要来了解结构体内存对齐的规则:

结构体的对齐规则:
1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员变量大小的较小值

- VS 中默认的值为 8
- Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小
3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

 所以在以上struct s2中一开始第一个成员c1的大小为1,比vs默认偏移数8小,所以偏移数就是1,c1大小为1

第二个成员n的大小为4,比vs默认偏移数8小,所以偏移数就是4,所以n从第五个字节开始,n大小为4字节

 第三个成员c2的大小为1,比vs默认偏移数8小,所以偏移数就是1,所以n从第九个字节开始,c2大小为1字节

 在此还未结束,因为结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍,该结构体struct s2最大对齐数为4,所以结构体大小必须是4的整数倍,以上的9字节不是4的整数倍,所以struct s2大小为12字节

 练习1

#include<stdio.h>
struct S3
{
	double x;
	char c;
	int n;
};
int main()
{
	printf("%zd\n", sizeof(struct s3));
	
	return 0;
}

以上代码的输出结果是什么呢?
 

x的大小为8个字节,偏移量为0,c的大小为1个字节,偏移量为9,n大小为4字节,偏移量为12,以上结构体大小为16字节满足结构体总大小为最大对齐数的整数倍

程序输出结果与分析结果相同

  练习2

#include<stdio.h>
struct S3
{
	double x;
	char c;
	int n;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
int main()
{
	printf("%zd\n", sizeof(struct S4));
	
	return 0;
}

 以上代码的输出结果是什么呢?

在以上代码就涉及到了结构体嵌套问题,在struct S4中c1的大小为1个字节,偏移量为0,因为在以上已经求出了struct S3的大小为16字节,我们需要再了解一个规律如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍,所以 s3偏移量为8,大小为16字节,d的大小为8个字节,偏移量为24,最终结构体struct S4大小就为32个字节,符合结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

 

2.2 为什么存在内存对齐

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

例如在以上结构体struct S中如果机器每次内存读取4个字节,在未对齐情况下就要读取两次,而在对齐的情况下就只要读取一次 


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

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起

struct s1
{
	char c1;
	char c2;
	int n;
};

struct s2
{
	char c1;
	int n;
	char c2;
};

例如在以上两个结构体中成员都一样,s1将占空间小的成员集中在了一起就使得结构体所占空间小了一些

2.3 修改默认对齐数

在以上的学习中我们知道了VS的默认对齐数是8,那么有什么办法可以修改默认对齐数呢?
其实是有办法的,利用#pragma 这个预处理指令,可以改变编译器的默认对齐数。

#include<stdio.h>
#pragma pake(1)//修改默认对齐数为2
struct s1
{
	char c1;
	char c2;
	int n;
};
#pragma pake()//恢复默认对齐数
int main()
{
	printf("%zd", sizeof(struct s1));
	return 0;
}

 使用了#pragma后默认对齐数就被修改为了2,这时以上结构体struct s1的大小就变成6字节

3.结构体传参

#include<stdio.h>
struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	for (int i = 0; i < 4; i++)
	{
		printf("%d ", s.data[i]);
	}
	printf("\n");

	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	for (int i = 0; i < 4; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("\n");
	printf("%d\n", ps->num);
}
int main()
{
	
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

我们知道在函数传参存在传值调用与传址调用,在结构体传参当中也是存在这两种方式的,以上代码中print1就是使用了传值调用,而print是使用了传址调用 ,两种方式都能找到结构体变量s以及其成员,但在结构体传参时一般是使用传址调用,原因是使用传址调用不用再开辟一块内存空间,新创建的指针就指向了原来的那块内存空间,而使用传值调用时,需要再创建一块新的内存空间,如果这个结构体像以上struct S一样所占内存空间很大的话,再创建新的内存就很浪费内存,还有函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

结论:
结构体传参的时候,要传结构体的地址

  

4. 结构体实现位段 

 4.1 什么是位段

位段的声明和结构是类似的,有两个不同:
1. 位段的成员必须是 int、unsigned int signed int ,在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。

例如以下结构体就是使用了位段

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

在此结构体中如果成员a知道自存0~3的整数,就可以在a后加上位段,2,这也就表示a成员只占2比特位,而用来a变量位int类型是占32个比特位,位段后就节省了许多空间,同理b,c,d也分别加上位段5,10,30

 

那么这和不使用位段的结构体大小有什么差别吗?

struct A
{
int _a;
int _b;
int _c;
int _d;
};

我们通过使用sizeof计算看看

 

这时就可以发现使用位段可以节省空间

那么为什么使用位段后以上结构体大小为8字节呢?这时我们就要学习位段的内存分配
 

4.2位段的内存分配

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

例如以下结构体

struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};

 这时我们知道成员a所占大小为3比特位,类型为char,大小为8比特位,这时就存在一个问题在给了成员变量后在空间内部是从左向右使用还是从右向左使用?这时假设是从右向左使用,这时又存在一个问题了当剩下空间不满足存放下一个成员时候,空间是浪费还是使用呢?这里假设是浪费

 

这时我们创建一个struct S类型的变量并给各成员初始化为0,再给其赋值调试程序观察内存

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;
}


 

存放入成员变量a的数是10二进制表示形式是1010,但成员a空间大小只有3比特位,所以存入是010, 存放入成员变量b的数是12二进制表示形式是1100,所以存入是1100, 存放入成员变量c的数是3二进制表示形式是11,所以存入是00011, 存放入成员变量d的数是4二进制表示形式是100,所以存入是0100

这时用16进制表示内存当中就为62 03 04

通过调试,查看内存与以上分析结果相同

 4.3 位段的跨平台问题

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

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

 

4.4 位段使用的注意事项 

位段的几个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。

 

正如以上的结构体中的a成员就没有相应的地址


所以不能对位段的成员使用&操作符,这样就不能使用scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main()
{
struct A sa = {0};
scanf("%d", &sa._b);//这是错误的
//正确的⽰范
int b = 0;
scanf("%d", &b);
sa._b = b;
return 0;
}

4.5位段的应用 

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要几个bit位就能描述,这⾥使用位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报大小也会较小⼀些,对⽹络的畅通是有帮助的。

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

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

相关文章

[数据集][目标检测][数据集][目标检测]智能手机检测数据集VOC格式5447张

数据集格式&#xff1a;Pascal VOC格式(不包含分割的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;5447 标注数量(xml文件个数)&#xff1a;5447 标注类别数&#xff1a;1 标注类别名称:["phone"] 每个类别标注的框数&#xff…

WPF -> MVVM

1.1安装MVV MLight 打开 Visual Studio 2022。 在顶部菜单栏中选择“工具” -> “NuGet 包管理器” -> “程序包管理器控制台”。 在控制台中输入以下命令&#xff0c;并按回车键运行&#xff1a; Install-Package MvvmLightLibsStd104.等待安装完成后&#xff0c;你就…

man命令的作用

man命令是Linux操作系统中一个非常实用的命令&#xff0c;它用于查看命令的手册页面&#xff0c;帮助用户了解特定命令的用法、选项和参数。这不仅对新用户在学习如何使用新命令时很有帮助&#xff0c;也方便了经验丰富的用户快速查找命令的详细信息。以下是具体介绍&#xff1…

基于java18多端展示+ idea hbuilder+ mysql家政预约上门服务系统,源码交付,支持二次开发

基于java18多端展示 idea hbuilder mysql家政预约上门服务系统&#xff0c;源码交付&#xff0c;支持二次开发 家政预约上门系统是一种通过互联网或移动应用平台&#xff0c;为用户提供在线预约、下单、支付和评价家政服务的系统。该系统整合了家政服务资源&#xff0c;使用户能…

LeetCode 算法:无重复字符的最长子串c++

原题链接&#x1f517;&#xff1a;无重复字符的最长子串 难度&#xff1a;中等⭐️⭐️ 题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的最长子串的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所…

谷歌浏览器的平替,内置开挂神器,我已爱不释手!

油猴浏览器正式版是一款基于谷歌Chromium源码开发的浏览器&#xff0c;它集成了集成了强大的油猴扩展&#xff08;Tampermonkey&#xff09;&#xff0c;使得用户可以轻松安装各种脚本&#xff0c;从而增强网页浏览体验。提供了一个更加个性化和高效的浏览体验。 油猴扩展&…

【Python网络爬虫】详解python爬虫中URL资源抓取

&#x1f517; 运行环境&#xff1a;PYTHON &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

CS和msf的权限传递,利用mimikatz抓取win10明文密码

一、Cobaltstrike的安装 http://t.csdnimg.cn/yhZin 安装CobaltStrike&#xff0c;浏览博主的上篇文章即可&#xff01;&#xff01;&#xff01; 这里我在自己的本机win11上执行了Client去连接kali中的Server端&#xff0c;直接执行.cmd文件即可&#xff01;&#xff01;&…

AI智能语音机器人系统如何对接科大讯飞接口

关于AI语音机器人的介绍有很多&#xff0c;但是由于商业化&#xff0c;没有一个能真正说明白的&#xff0c;当然&#xff0c;我们搭建的AI智能机器人系统也是商业化的&#xff0c;毕竟业务是做这方面的&#xff0c;但是价格绝对是公道的&#xff0c;废话不多说了&#xff0c;我…

C++vector及其实现

第一个参数是类型(可以是自定义也可以是内置类型) 相当于生成一个该类型的数组 allocator是空间配置器 遍历 1.下标遍历 2.迭代器遍历 3.范围for 对象访问 有名对象访问 匿名对象访问 隐式类型转换 成员函数 sort 使用sort需要包含头文件algorithm eg. sort的使用非…

QA 未能打开位于 D:/Computer999/Computer999.vbox 的虚拟电脑

前言 未能打开位于 xxx/Computer999.vbox 的虚拟电脑&#xff0c;并提示E_INVALIDARG (0X80070057)&#xff0c;是最常见的一个错误&#xff0c;下面是解决办法。 内容 1、提示下面的错误&#xff0c;注册Computer999失败&#xff1a; 未能打开位于 D:/Computer999/Compute…

【刷题(15】普通数组

一 普通数组基础 首先&#xff0c;我们根据下图先了解一下什么是前缀和。 既然我们明白了前缀和是怎么回事&#xff0c;那我们就来看一下我们该怎么输入 先给出答案&#xff0c;然后再给出分析。 答案&#xff1a; for (int i 1; i < n; i ){cin >> a[i];s[i] s…

JVM之垃圾回收面试总结

文章目录 1.GC概述1.1 什么是垃圾1.2 为什么需要GC&#xff1f;1.3 早期垃圾回收1.4 Java垃圾回收机制1.5 评估GC的性能指标 2.垃圾回收相关算法2.1 垃圾标记阶段的算法2.1.1 引用计数算法(Java没有使用)2.1.2 可达性分析算法 2.2 垃圾清除阶段的算法2.2.1 标记-清除(Mark-Swee…

今在推特发一个推特立马推特账户被删除了

咨询 Google Contacts 是如何 获取我的苹果手机通讯录的电话号码清单的&#xff1f;不到一分钟&#xff0c;我的账户之间被删除了&#xff0c;比停用、冻结还令人可怕。 立马推特账户被删除了。

阿里云搭建物联网平台+MQTT.fx接入阿里云

文章目录 本篇介绍一、阿里云物联网平台搭建二 、MQTT客户端接入阿里云物联网平台总结 本篇介绍 本篇搭建了阿里云物联网平台&#xff0c;使用MQTT.fx接入阿里云&#xff0c;上传温湿度数据 使用到的软件&#xff1a;阿里云、MQTT.fx 一、阿里云物联网平台搭建 首先创建一个物…

Codeforces Round 949 (Div. 2)(A,B题解)

这场真是给我打的汗流浃背了&#xff0c;这场真的巨难&#xff08;可能是因为我二进制根本就没学好的原因吧&#xff09; 反正总共就搞了两道题&#xff0c;第一道题注重于思维&#xff0c;第二道题纯二进制&#xff0c;第三道题看着也是二进制&#xff08;最后时间不够了&…

【Microelectronic Systems】

1 background&introduction 2 analog to Digital Conversion 3 Digital to Anolog Conversion 4 introduction to CMOS outlook ! introduction to semiconductors siemens,西门子 properties of semiconductors types of semiconductors $ PN junction 5 Mathematic s…

Llama(二):Open WebUI作为前端界面,使用本机的llama3

目录 背景 Open WebUI是什么 工程能力特性 产品功能特性 用户体验特性 Open WebUI安装并使用 背景 Mac M1芯片&#xff0c;16G 内存 llama3 8B的部署参考Llama&#xff08;一&#xff09;&#xff1a;Mac M1芯片运行Llama3-CSDN博客在Mac M1 16G内存环境中&#xff0c;…

【数据结构】二叉树的层序遍历~动画超详解

目录 1 什么是层序遍历2 二叉树层序遍历的基本思路3 二叉树层序遍历的实现 1 什么是层序遍历 我们从字面意思就明白,所谓层序,就是一层一层按顺序去遍历一个二叉树,这和我们之前了解的按前中后序遍历方式完全不同 比方说这颗二叉树: 前序遍历: 层序遍历: 2 二叉树层序遍历的…

Zabbix安装:构建高效可靠的Zabbix监控系统

目录 引言 一、zabbix基本介绍 &#xff08;一&#xff09;什么是zabbix &#xff08;二&#xff09;zabbix结构体系 &#xff08;三&#xff09;zabbix监控对象 &#xff08;四&#xff09;zabbix进程 &#xff08;五&#xff09;zabbix监控模式 &#xff08;六&#…