一、网安学习成长路线图
网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、网安视频合集
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
三、精品网安学习书籍
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、网络安全源码合集+工具包
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、网络安全面试题
最后就是大家最关心的网络安全面试题板块
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
#define MAX 100
int main()
{
int m = MAX;
return 0;
}
`#define`定义的符号在预编译期间会完成替换。如图所示:
![](https://img-blog.csdnimg.cn/ed3ef6ab728548ffa3f8c7657edfec9d.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
###### 注意
* `#define`定义标识符时,最好不要在最后加上`;`
>
> 若加上`;`,那么`;`也就是标识符内容的一部分。这样会在实际代码中多出一个分号,空语句。
>
>
>
![](https://img-blog.csdnimg.cn/7db93b29504a4a88a7d9797587add669.png#pic_center)
* 当定义类型时,`#define`和`typedef`的区别
`#define`和`typedef`一个是定义标识符,一个是定义类型,二者本身并无任何联系。
#define INT int
typedef int int_t;
当#define定义类型时,除了语法形式不同外,
#define定义的`INT`是个标识符,在预处理阶段就被替换成`int`。typedef定义的`int_t`本身编译器认定为类型,编译到运行都不会变。
###### #define 定义的宏
`#define`定义宏和标识符常量的区别是宏有参数。将参数替换到文本中,这种实现被称为宏。
//声明形式
#define Name(para1,…) stuff
>
> 参数列表需紧靠左边宏名,不然会被解析为宏体的一部分。
>
>
>
宏形式类型于数学中的函数
f
(
x
)
=
x
2
f(x)=x^2
f(x)=x2 ,都是将参数带入计算结果。如图:
![](https://img-blog.csdnimg.cn/4df8ac2434684a18825ab01ce45a7f57.png#pic_center)
###### 错误形式
//1.
#define SQUARE(x) x*x
int main()
{
int ret = SQUARE(5 + 1);
printf(“%d\n”, ret);
return 0;
}
上述代码,计算的结果并非36,而是11。因为在替换的过程中`SQUARE(5+1)`替换成立`5+1*5+1`,遂得11。
为避免参数为表达式时由运算符优先级差异而产生歧义,需要对宏体中的单项`x`加`(x)`。
//2.
#define DOUBLE(x) (x)+(x)
int main()
{
int ret = 2 * DOUBLE(5);
printf(“%d\n”, ret);
return 0;
}
上述代码计算结果也不是我们想要的`2*(5+5)=20`,而是`2*5+5=15`。这次是宏名外的运算符产生的歧义,故得出宏体整体还需加`()`。
所以正确的写法为
#define DOUBLE(x) ((x)+(x))
正确形式是:宏体中的单项参数和整个宏体都需要加上`()`。
###### #define 的替换规则
1. 宏调用时,首先检查并替换参数和宏体中用#define定义的符号。
2. 然后再将宏和参数的值替换过去。
3. 扫描结果文本,若仍包含#define定义内容,就重复上述处理。
###### 注意
* 宏参数和宏体中允许出现其他#define定义的宏或标识符。但宏不允许递归。
* 预处理器搜索#define定义符号时,字符串常量中的内容不被搜索。
###### 宏操作符 `#`和`##`
`#`可以将参数插入字符串中。
int a = 10;
printf(“The value of a is %d\n”, a);
int b = 10;
printf(“The value of b is %d\n”, b);
int c = 10;
printf(“The value of c is %d\n”, c);
如这样的代码,我们如何将自动将字符串中的a,b,c替换而不用每次都修改字符串呢?
首先,C语言中两个字符串放在一起会自动视为一个字符串,如:
printf(“Hello world\n”);
printf("Hello "“world\n”);
当然\*\*`#`的作用是将`#`后面的参数转化成对应的字符串\*\*,如果前后都是字符串,那么自动拼接为一个字符串。
这样上述需求我们就找到了解决方法。
#define PRINT(n) printf(“The value of “#n” is %d\n”,n);
int main()
{
int a = 10;
PRINT(a);
int b = 20;
PRINT(b);
}
首先传参将n替换为a,故`#a`被转化为字符串`"a"`。`PRINT(a)`会被替换成`printf("The value of ""a"" is %d\n",a)`。
**`##`将位于其两边的符号合成一个符号。**
#define CAT(X,Y) X##Y
int main()
{
int class102 = 100;
printf(“%d\n”, CAT(class,102));//100
printf("%d\n", CAT(1, 0));//10
CAT(class, 102) = 200;
printf("%d\n", CAT(class, 102));//200
return 0;
}
可见,拼接起来的不仅可以视为符号,也可以视为数字,字符串等。个人认为既然`##`拼接行为是在预处理阶段完成的,对于正在编译的代码来说`##`合成的结果和代码敲出来的是一样的。
>
> 宏操作符`#`和`##`只能在宏中使用。
>
>
>
###### 带副作用的宏参数
宏的参数传入一些带有副作用的操作符,可能会导致一些未知的错误。
a = 1;
//1.
b = a + 1;//b=2, a=1
//2.
b = a++;//b=2, a=2
如此,二者相比b虽然都是2,但后者a自增了1,这就是带有副作用的表达式。
//1. 宏
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
//2. 函数
int Max(int x, int y) {
return x>y?x:y;
}
int main()
{
int a = 20;
int b = 10;
int m1 = MAX(a++, b++);
int m2 = Max(a++, b++);
return 0;
}
![](https://img-blog.csdnimg.cn/050e0e9cc43841a39910260fe4c46443.png#pic_center)
>
> 因为都是后置`++`,所以`a++`,`b++`的值还是20和10,当然判断之后`a`,`b`的值分别+1,整个表达式的值就是后面的`a++`的值即21,然后a的值又+1,当然后面b++的表达式不执行。
>
>
>
可以看出,宏的参数是不计算,直接预编译时整体替换后在编译期间计算的。而函数传参同样因为后置++,而传的是`a++`,`b++`的值,传完之后a,b分别+1。
###### 宏和函数的对比
>
> 宏常被用于执行相对简单的运算,正如上面的例子。当然函数同样也能执行这样的任务,如何选择,请看下列二者优劣的分析。
>
>
>
宏的优势:
1. 使用函数要建立栈帧,销毁栈帧,一系列的准备工作比实际任务大得多。故宏在程序规模和执行速度方面更胜一筹。
2. 函数参数必须声明类型,且只能适用一种类型,而宏无类型检查,只要满足运算的类型都可以作参数。
宏的劣势:
1. 每次调用宏时,都会将宏的代码替换到调用处。若宏代码量大,可能会大幅增加代码长度。
2. 宏替换发生在预编译期间,故无法调试。
3. 宏的类型无关性,也会导致其不够严谨。
4. 宏可能由于运算符优先级的问题,会导致程序出错。
>
> 当然宏可以做到函数做不到的事情,如宏的参数可以是类型。下列宏`offsetof`计算成员的偏移量的模拟实现。
>
>
>
#define offsetof(StructType, MemberName) (size_t)&(((StructType*)0)->MemberName)
| 分类 | 宏 | 函数 |
| --- | --- | --- |
| 代码长度 | 宏代码插入后,程序长度可能**大幅增加** | 函数代码仅存一份,每次调用同一位置 |
| 执行速度 | 简单更快 | 栈帧的创建和销毁的**额外开销** |
| 操作符优先级 | 周围表达式中操作符优先级可能会致错,故要**加全括号** | 参数在调用处求值一次并传递表达式的值 |
| 参数副作用 | **直接替换**后再对参数进行处理,副作用的参数可能会致错 | 参数在传参处求值后再传参处理数据 |
| 参数类型 | 宏参数与**类型无关**,在操作合法的情况下,适用于任意类型 | 函数参数受类型限制,参数类型不同需要不同的函数 |
| 调试 | 宏**无法调试** | 函数可以调试 |
| 递归 | 宏**无法递归** | 函数可以递归 |
>
> 所以对于二者的好坏我们要辩证的看待。
>
>
>
###### 命名规范
宏与函数的使用方式很类似,语法无法将二者区分开来。故一般规定宏名字母全部大写,而函数采用大小驼峰形式。
>
> 命名规范是约定俗成的东西,真正凸显实力的是写出效率高量少的代码,而不是任性违背规范。
>
>
>
##### #undef
`#undef`用于移除宏定义。故一般和`#define`搭配使用。
#define MAX 100
int main()
{
int a = MAX;
#undef MAX
//int b = MAX;Err
return 0;
}
这样可以使预定义符号`MAX`在不同的代码处,可以拥有不同的定义。先移除再重新定义即可。
##### 命令行定义
命令行定义是指在启动编译时对代码文本中的符号进行定义。
![](https://img-blog.csdnimg.cn/215999d725404a719a7e571ff33df9c7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_10,color_FFFFFF,t_70,g_se,x_16#pic_center)
如上列代码所示,数组大小`SZ`未定义,我们可以在编译该源文件时添上对`SZ`的定义:`gcc test.c -D SZ=10`
![](https://img-blog.csdnimg.cn/fd549d2a2e924c1686ae37159fbaaeb6.png#pic_center)
根据不同的情况给变量赋不同的值。这使得对于同一段代码编译出不同结果时,更加方便。
##### 条件编译
条件编译指令使得让某段代码参与或不参与编译的操作变得相对容易,类似于注释代码,达到选择性编译的效果。
###### 常见编译指令
常见的条件编译指令如下,类似于if语句也有单分支多分支的情况:
//1.
#if 常量表达式
#endif
//2.
#if 常量表达式
#elif 常量表达式
#else
#endif
`#if,#elif,#else`类似于if语句结构,`#endif`用于结束条件编译。
//单分支
int main() {
#if 1
printf(“haha\n”);
#endif
#if 0
printf(“hehe\n”);
#endif
return 0;
}
//多分支
int main() {
#if 12
printf(“hehe\n”);
#elif 23
printf(“haha\n”);
#else
printf(“…\n”);
#endif
return 0;
}
满足条件则执行,不满足条件则不执行。注意条件只能是常量表达式,因为预编译指令只在预处理阶段中起作用,而变量是在运行期间创建的。
还有更特殊化的条件编译指令,单独用于判断符号是否被定义,如`#if defined`,`#if !defined`等。
//3.1
#if defined (symbol)
#endif
//3.2
#ifdef symbol
#endif
//4.1
#if !defined(symbol)
#endif
//4.2
#ifndef symbol
#endif
语法规定每一个条件编译指令`#if...`都要搭配上`#endif`使用。
#define MAX 100
int main() {
//1.定义
#if defined (MAX)
printf(“haha\n”);
#endif
#ifdef MAX
printf(“hehe\n”);
#endif
//2.未定义
#if !defined (MAX)
printf(“dada\n”);
#endif
#ifndef MAX
printf(“titi\n”);
#endif
return 0;
}
* `#if define..`代表当其后条件满足时,执行下面语句,`#ifdef..`是其简写形式。
* `#if !define..`代表当其后条件不满足时,执行下面语句,`#ifndef..`是其简写形式。
###### 嵌套指令
#define SBL 100
#define OPTION 100
int main() {
#if defined (SBL1)
#ifdef OPTION1
option1();
#endif
#ifdef OPTION2
option2();
#endif
#elif defined (SBL2)
#ifdef OPTION3
option3();
#endif
#ifdef OPTION4
option4();
#endif
#endif
return 0;
}
![](https://img-blog.csdnimg.cn/380d348f47f74247a57dfa9d64e31066.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQUtB5L2g55qE6Ze66Jyc,size_16,color_FFFFFF,t_70,g_se,x_16#pic_center)
同样条件编译指令也是预处理指令,预处理后自然将不满足条件的内容删去。
##### 文件包含
本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。
最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。
最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。
![](https://img-blog.csdnimg.cn/img_convert/311903982dea1d8a5d2c98fc271b5b41.jpeg)
### 学习路线图
其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。
相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。
![](https://img-blog.csdnimg.cn/img_convert/1ddfaf7dc5879b1120e31fafa1ad4dc7.jpeg)
#### 网络安全工具箱
当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份**我自己整理的网络安全入门工具以及使用教程和实战。**
![](https://img-blog.csdnimg.cn/img_convert/bcd1787ce996787388468bb227d8f959.jpeg)
#### 项目实战
最后就是项目实战,这里带来的是**SRC资料&HW资料**,毕竟实战是检验真理的唯一标准嘛~
![](https://img-blog.csdnimg.cn/img_convert/35fc46df24091ce3c9a5032a9919b755.jpeg)
#### 面试题
归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**