2024年最新C语言进阶:自定义类型_自定义函数类型(1),2024年最新网络安全基础72问

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以点击这里获取

struct S1 s = { 'x',100,'y' };
printf("%d\n", sizeof(struct S1));//12
printf("%d\n", sizeof(struct S2));//8
return 0;

}


上述代码可以,看出结构体的成员变量位置不同,结构体大小也不同。至于原因,且看下文分解:


###### 内存对齐的规则


1. 结构体第一个成员永远位于结构体距起始位置偏移量为0的位置。



> 
> 即首个成员一定放在为结构体所开辟的内存空间的第一个位置。
> 
> 
> 


2. 从第二个成员开始,各自放在偏移量为该变量对齐数的整数倍处。对齐数为变量自身大小和编译器默认对齐数的较小值。



> 
> Linux环境下无默认对齐数,Windows环境下对齐数为8。而一般无变量类型所占字节大于8,故对齐数一般为变量的自身大小。
> 
> 
> 


3. 结构体的总大小必须为所有成员变量的对齐数的最大值的整数倍。



> 
> 笔者猜测是为了凑齐读取域宽的整数倍,不至于让之后创建的变量紧随其后而造成不必要的麻烦。
> 
> 
> 


4. 若结构体嵌套,内嵌结构体对齐到其成员最大对齐数的整数倍处,整体结构体总大小须为其成员最大对齐数的整数倍。



> 
> 由第3条可推得,**内嵌结构体和整个结构体同样都是结构体,都要对齐到各自成员变量对齐数的最大值的整数倍处**。而一般整个结构体的最大成员变量都是内嵌结构体。
> 
> 
> 


偏移量:距起始位置的字节个数,相当于下标位置。如第1个字节的偏移量为0,第2个字节的偏移量为1。


现在再来看上面的例子:



//1.
struct S1 {
char c1;
int a;
char c2;
};
//2.
struct S2 {
char c1;
char c2;
int a;
};


* `c1`放在偏移量为0的地址处,`a`的对齐数为4则放在偏移量为4的地址处浪费3个字节,次之`c2`对齐数为1偏移量为任意位置故紧随其后。共占9个字节,但总大小需为4的倍数,所以再浪费3个字节,共12个字节。
* `c1`和`c2`对齐数都为1,随后`a`对齐数为4故放在偏移量为4的地址处,正好一共8个字节。


![](https://img-blog.csdnimg.cn/5509d54f3104462c8e6aabe7cd1f6c37.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)


###### Example


求出下列结构体所创建的变量的大小。



//3.
struct S3 {
double d;
char c;
int a;
};
//4.
struct S4 {
char c1;//1
struct S3 s;//8
double d;//8
};


![](https://img-blog.csdnimg.cn/4387bc91014446838b51591cd255757e.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)


* 嵌套结构体所占内存为16个字节,但其最大对齐数为8,所以整个结构体的成员变量对齐数的最大值即为8。


###### 存在内存对齐的原因



> 
> 这样的内存对齐的机制,显得又浪费空间又使得计算繁琐,但是它的存在是有很必要的,虽没有官方明确的解释,但是也可总结为以下两点:
> 
> 
> 


1. 移植原因


不是所有硬件平台都能任意的读取地址上的任意数据。某些平台只能在特定的地址处以特定的方式读取特定的数据。如只在地址为4的倍数处读取,且每次读取4个字节的数据。平台之间移植性差。


2. 性能原因


数据应尽可能地存储在地址的自然边界上并对齐,以防止同一块空间的数据要作两次访问,提升读取数据的效率。



> 
> 总结就是内存对齐是为了牺牲空间复杂度降低时间复杂度,以空间换取时间。当然我们要做的就是尽己所能既节省空间又节省时间
> 
> 
> 


结构体中不同的变量放在不同的位置,结构体所占的大小不同。**让占用空间小的成员集中在后面**,可以是实现一定程度上的节约空间。


###### 默认对齐数的修改



//设置默认对齐数
#pragma pack(n);

struct Tag {
member_list;
};

//恢复默认对齐数
#pragma pack();


默认对齐数是可以被修改的,使用前设置,使用后取消。当认为结构体的默认对齐数不适当时,可自行设置。同时对齐数  
 
 
 
 
 n 
 
 
 
 n 
 
 
 n 一般都设置为  
 
 
 
 
 
 2 
 
 
 n 
 
 
 
 
 2^n 
 
 
 2n 。


###### Example


实现宏计算结构体中某变量相对于首地址的偏移量。



#include <stddef.h>
struct S1 {
char c1;
int a;
char c2;
};
int main()
{
printf(“%d\n”, offsetof(struct S1, c1));
printf(“%d\n”, offsetof(struct S1, c2));
printf(“%d\n”, offsetof(struct S1, a));
return 0;
}


 


#### 位段


##### 位段的定义


位段的声明和结构体类似,但又两点不同。


1. 类型不同:位段的成员必须是整型变量,如`char,int,unsigned int`等。
2. 写法不同:位段的成员名后使用`:`和`数字`来规定分配的空间。如:



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


计算位段`A`的大小得8,而4个整形变量最小占16个字节。说明**位段一定程度上可以节省空间**。


位段中的“位”表示二进制位,而`:`后的数字代表系统分配给该变量的比特位数。



> 
> 在描述对象时,属性变量中的所有位数不一定全部使用,使用位段可以规定系统分配给变量的空间。当然数据过大仍会溢出。
> 
> 
> 


##### 位段的内存分配


* 系统按成员变量类型来为位段开辟空间,一次性开辟一个变量类型大小的空间。



> 
> 如该成员为`int`型,则一次开辟4个字节,若不够则再开辟4个字节。若为`char`类型,则开辟1个字节。
> 
> 
> 


* 位段使用时涉及很多不确定因素,程序可移植性差,故位段是不跨平台的。


![](https://img-blog.csdnimg.cn/6eba89202f0d4d4da1d8d1c247a212f9.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)



> 
> 如图所示,先开辟4个字节的空间,`a`占用2bit,`b`占用5bit,`c`占用10bit。这4个字节还剩15个bit不够`d`的存放,必然要在开辟4个字节的空间。这就是算出来的8个字节。
> 
> 
> 


问题是`d`接着一半存放在第一个字节一半存放在第二个字节,还是全部存放在新开辟的空间内?



> 
> 不同的编译环境下可能会产生不同的结果,这是C标准中未规定的内容。笔者在此仅考虑Windows环境的情况,请看接下来的例子。
> 
> 
> 



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



> 
> 对位段变量进行赋值操作,就又带来了一个问题单个字节内先使用高地址还是低地址?这也是标准未规定的。
> 
> 
> 我们先进行假设:位段中先使用高地址再使用低地址,同时剩余空间不足则将其抛弃并重新开辟。如果vs中的最后结果和预期一致,则假设正确。
> 
> 
> 


![](https://img-blog.csdnimg.cn/4b66387c70a24800a35e02423e584086.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)


我们按照假设写出位段的内存情况:  
  
 
 
 
 
 ( 
 
 
   
 
 
 0110 
 
 
   
 
 
 0010 
 
 
   
 
 
 0000 
 
 
   
 
 
 0011 
 
 
   
 
 
 0000 
 
 
   
 
 
 0100 
 
 
   
 
 
 
 ) 
 
 
 2 
 
 
 
 
 ( 
 
 
 
 6 
    
 
 
 2 
 
 
 
 0 
 
 
 
 3 
 
 
 
 0 
 
    
 
 4 
 
 
 
 
 ) 
 
 
 10 
 
 
 
 
 (\ 0110\ 0010\ 0000\ 0011\ 0000\ 0100\ )\_{2} \\(\quad 6 \;\quad 2 \qquad 0 \qquad 3 \qquad 0 \quad\; 4\quad)\_{10} 
 
 
 ( 0110 0010 0000 0011 0000 0100 )2​(620304)10​  
 ![](https://img-blog.csdnimg.cn/d24b40d98ce04c49be60ed190a347aa1.png#pic_center)



> 
> vs显示结果和我们的假设完全相符。故假设正确。所以可以得出结论,在vs环境下:
> 
> 
> 


1. 每次开辟空间所开辟的字节个数,由需开辟空间的成员变量的类型所决定。
2. 内存使用时,先使用低字节再使用高字节,单个字节内从高位到低位使用。
3. 所开辟内存空间不足时,抛弃剩余内存,重新开辟类型大小的空间。



> 
> 由于这些规则C标准并未明确规定,因而这些结论因编译器而异。所以位段的平台移植性差。
> 
> 
> 


##### 位段的跨平台问题


1. `int`位段的最高位是否被当做符号位不确定。
2. 位段中成员类型的所占比特位数目不确定。



> 
> 早期16位机器`int`占2个字节共16个比特位,而变量分配bit位数目不得多于最大值。
> 
> 
> 


3. 位段成员在内存中先使用高地址还是低地址不确定。
4. 所开辟内存空间不足时,是否抛弃剩余内存重新开辟还是接着使用剩余内存不确定。


##### 位段的应用


和结构相比,位段可达到同样的效果,可以节省空间,但是需使用小心且跨平台性差。而位段可以应用到网络协议中,不至于浪费大量的空间,网络传输协议中每几个比特位成一组用于传输不同的数据。


 


#### 枚举类型



> 
> 枚举顾名思义一一列举,有很多数据可以列举出来,如:性别,月份,颜色等。
> 
> 
> 


##### 枚举的定义



enum Tag {
con1,
con2,

con3
};


* `enum`是枚举关键字,`Tag`是枚举对象名;
* `con1,con2,...,con3`是枚举常量列表。



> 
> 同时枚举就相当于整形常量,故所有枚举常量都是4个字节。
> 
> 
> 



//星期
enum Day {
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
//性别
enum Sex {
FAMALE,
MALE,
SECRET
};
//颜色
enum Color {
RED,
GREEN,
BLUE
};



> 
> 上述定义的`enum Day`,`enum Sex`,`enum Color`都是枚举类型。`{}`内是枚举类型的可能取值,即枚举常量。
> 
> 
> 


枚举常量取值默认从0开始,依次递增。也可进行(完全或不完全)初始化对其赋初值,所初始化常量之前的常量取值不受影响,之后的常量仍然依次递增。


![](https://img-blog.csdnimg.cn/7ee6bff286df4570aa1252e6bdf3dd84.png#pic_center)


当然常量只能进行初始化,而不能进行赋值操作。



//1.
enum Color c = GREEN;
//2.
enum Color c = 1;


上述操作为创建枚举类型的变量,赋值为`GREEN`。



> 
> C语言对语法的检测没有那么严格。所以1和2都行。在C++中认为`1`是字面常量而`GREEN`为枚举常量。二是不相等的,所以不能赋值。
> 
> 
> 


##### 枚举的优点


1. 提高代码可读性和可维护性



> 
> `#define`定义的常量不如枚举常量有意义,且枚举常量是具有类型的更严谨。
> 
> 
> 


2. 防止命名污染



> 
> `#define`定义的常量属于全局常量,易冲突。
> 
> 
> 


3. 便于调试



> 
> `#define`定义的常量在预编译期间就已经被替换,该常量已不复存在。而枚举类型一直存在有值有类型便于调试。
> 
> 
> 


4. 使用方便



> 
> 一次可定义多个常量,且便于管理。
> 
> 
> 


##### 枚举的使用



/*
* 计算器
* 使用枚举常量
* */
enum Option {
EXIT,//0
ADD,//1
SUB,//2
MUL,//3
DIV,//4
};

int Add(int x, int y) {
return x + y;
}
int Sub(int x, int y) {
return x - y;
}
int Mul(int x, int y) {
return x * y;
}
int Div(int x, int y) {
return x / y;
}
void Calc(int (*pf)(int, int)) {
int a = 0;
int b = 0;
printf(“请输入操作数:>”);
scanf(“%d %d”, &a, &b);
printf(“%d\n”, pf(a, b));
}

void menu() {
printf(“*************************\n”);
printf(“**** 1.ADD 2.SUB ****\n”);
printf(“**** 3.MUL *** 4.DIV ****\n”);
printf(“******* 0.exit ********\n”);
printf(“*************************\n”);
}
int main()
{
int input = 0;

do {
	menu();
	printf("请选择\n");
	scanf("%d", &input);
	switch (input) {
	case ADD:
		Calc(Add);
		break;
	case SUB:
		Calc(Sub);
		break;
	case MUL:
		Calc(Mul);
		break;
	case DIV:
		Calc(Div);
		break;
	case EXIT:
		break;
	default :
		printf("选择错误\n");
		break;
	}
} while (input);
return 0;

}


 


#### 联合体


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

t \*\*\*\*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
}
int main()
{
	int input = 0;

	do {
		menu();
		printf("请选择\n");
		scanf("%d", &input);
		switch (input) {
		case ADD:
			Calc(Add);
			break;
		case SUB:
			Calc(Sub);
			break;
		case MUL:
			Calc(Mul);
			break;
		case DIV:
			Calc(Div);
			break;
		case EXIT:
			break;
		default :
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

联合体

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值