当前位置: 首页 > news >正文

函数(子程序)的常见、易混淆概念详解【对初学者有帮助】

C语⾔中的函数也被称做子程序,意思就是⼀个完成某项特定的任务的⼀小段代码。

C语⾔标准中提供了许多库函数,点击下面的链接可以查看c语言的库函数和头文件。

C/C++官⽅的链接:https://zh.cppreference.com/w/c/header

目录

一、函数头与函数体

二、实参与形参

三、return的用法事宜

四、main的返回类型与参数(了解即可)

#1. main的返回类型

#2. main的参数个数

#3. 参数argc和argv(了解即可)

五、数组作为函数参数(重中之重)

#1. 数组传参的本质

#2. 不存在形式数组(下标操作符失效):

#3. 二维数组传参不能省略列

六、嵌套调用与链式访问

七、函数的声明与定义

#1. 声明与定义的区别 和 注意事项

#2. 未声明的函数 

#3. 头文件声明的底层逻辑 和 函数调用操作符()


一、函数头与函数体

 函数头 :即函数定义中的第一行代码,函数头内包含着(1)函数的返回类型、(2)函数的名字、(3)形式参数的类型和个数(即参数表)。

 函数体 :即函数定义中的大括号{ }部分,它是是真正调用形参、执行子代码、返回任务值的重要部分。

举例:执行加法运算的函数

int  add(int  a, int b)   //该行就是函数头(信息:函数名add,返回类型int,2个形参都是整型)

{                                        // (int a, int b) 是参数表

        return  a+b;    //整个大括号的内容就是函数体(这里负责加法运算和返回运算的结果)

}

二、实参与形参

实参的几种理解和概念:

1. main函数中的参数(包括常数和变量等)

2. 实参是在调用函数时传递给函数的具体参数值。

3. 实参变量在主函数被创建时会直接申请内存空间,程序结束时才归还内存空间。

实参的特点:

1. 作用域:可以在mian函数内使用,不能在函数(子程序)被使用

2. 生命周期:从变量创建到程序结束。(长时,仅一次)

形参的几种理解和概念:

1. 形参是函数定义中的参数,是一种占位符,用于接收传递给函数的数据。

2. 形参是实参在函数调用时的⼀份临时拷贝

3. 函数不被调用时,形参变量不会申请内存空间;调用时才申请空间,调用完就归还空间。

形参的特点:

1. 作用域:可以在函数(子程序)使用,不能在mian函数内被使用

2. 生命周期:从函数调用到函数结束。(短时,可多次)

总的来说,形参由实参而来,它们的讨论载体是函数传递

注意:在函数中创建的变量并不属于形参变量,而是局部变量,因为它的创建并没有传递关系

三、return的用法事宜

作用:return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式。return语句执⾏后,函数就彻底返回(有返回值的话带回返回值),后边的代码不再执行。(主函数也是如此)

return在下面4种情况的用法细则:

 i. 函数返回值是void:

因为无需返回类型,可以写成“ return ; ”。return后面除了英文分号,其他什么都不要写。

 ii. 函数返回值类型与return的值不同:

return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型。

比如函数的返回类型是整型,而return的任务值它的数据类型是浮点型,那么返回的结果是在任务值的基础上去掉小数部分,留下整数部分:

int f()
{return 1.5 + 3.7;
}
int main()
{int a = f();printf("返回值为:%d\n", a);return 0;
}

1.5 + 3.7等于5.2,但是返回类型是int,所以被隐式类型转换为5。函数返回值的隐式转换与算术的隐式类型转换是相同的,详细请看我这篇文章:《数学计算类操作符 和 算术类型转换》

 iii. 函数内容是分支结构的:

如果函数中存在 if 等分⽀的语句,则要保证每种情况下都有return返回,否则容易出现编译错误。

 iv. main函数中的return语句:

return 语句在 main 函数中的作用是结束程序的执行并返回一个整数值给操作系统。这个整数值通常用于表示程序的退出状态,其中0表示程序正常退出,非零值表示程序异常退出

返回非0值的错误信息:(了解即可)

  • 返回1——文件打开失败:如果程序试图打开一个不存在的文件或者没有权限访问的文件
  • 返回2——内存分配失败:如果程序在运行过程中无法分配足够的内存空间
  • 返回3——无效的输入参数:如果程序接收到无效的输入参数
  • 返回4——运行时错误:如果在程序执行过程中发生了除以零、数组越界等运行时错误

这里列举了4个错误的整型返回值,实际上还有很多种。

四、main的返回类型与参数(了解即可)

#1. main的返回类型

 i. " int  main() " 形:(标准)

这是C99中标准的main函数使用形式。int返回类型使得main函数能够向操作系统返回程序的退出状态,返回0通常表示程序正常退出,而返回非0值表示程序异常退出。(上面也有举例提及)

 ii. " main() " 形:

早期的C代码中(历史上),常常可以看到没有明确指定返回值的主函数,即main()。这样写是因为,函数定义不明确返回类型时系统会默认为int型,所以这样写也是返回一个整数值。

在C99的规定中,这是一种不标准的写法,建议不要这样写。

 iii. "void  main() " 形:

首先要明确的一点,这是一种错误的写法,历史上void main()也从未在C++标准或C标准中得到定义。

虽然返回值是void也能执行main函数里的内容,但是main函数也是会被其他函数调用的,而调用main函数的函数需要知道程序是否正常退出、异常退出的原因是什么。比如mainCRTStartup()函数与__mainCRTStartup()函数。{调用逻辑:mainCRTStartup()函数 —> __mainCRTStartup()函数 —> main()函数 }

#2. main的参数个数

虽然main函数可以不写参数,但其实参数表上是有参数的

 i. 一个参数

在某些平台上,main函数可能会接受一个参数,例如main(argc),但这种形式并不标准,且现在很少见。

 ii. 两个参数

这是最标准的main函数形式,接受命令行参数个数和参数数组,即int main(int  argc, char* argv[ ]) 。

 iii. 三个参数

在一些特定的系统或编译器扩展中,main函数可能会有第三个参数,用于传递环境变量,例如int main(int  argc, char* argv[ ], char* envp[ ])。这种形式不是标准形式,但在Windows等系统中比较常见。

#3. 参数argc和argv(了解即可)

  • argc是一个整型变量,表示传递给程序命令的参数数量程序名本身作为第一个参数。例如:如果你运行一个名为"program"的程序,并传递了两个参数"arg1"和"arg2",那么argc的值将是3。

  • argv是一个字符指针数组,其中每个元素指向一个字符串,这些字符串是传递给程序的命令行参数。除了argv[0]通常是程序的名称其他argv[n]代表第n-1个参数。比如:argv[1]是第一个参数,argv[2]是第二个参数,依此类推。
int main(int argc, char* argv[]) 
{printf("文件名: %s\n\n", argv[0]);printf("参数个数: %d\n", argc);return 0;
}

五、数组作为函数参数(重中之重)

#1. 数组传参的本质

数组传参的本质是传递该数组的地址(首元素地址),传递的地址值交给形式指针变量来管理。而并不是像形参变量那样,调用函数时会申请内存空间,数组的传递并不会开辟新空间,且函数操作的是原数组的内存空间。当然,函数调用的时候,会申请空间给形式指针变量的创建。

以下面的代码为例:(参数表中的arr其实并不是数组,而是指针,后面我会讲解)

void print_arr(int arr[], int n)
{for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}
int main()
{int a[5] = { 1,2,3,4,5, };size_t sz = sizeof a / sizeof a[0];print_arr(a, sz);return 0;
}

可以发现,arr的值是与a的值、&a的值和&a[0]的值是相等的,这说明函数调用时,数组a传参给函数并没有申请一块新的连续空间来拷贝数组a,而是用指针arr来对原数组a的内存空间进行操作

#2. 不存在形式数组(下标操作符失效):

先说结论:其实参数表中的接收数组,并不是真正的数组(因为函数并未对原数组进行拷贝),而是一个指针。由于这是形式参数,可以称之为形式指针

补充知识:数组也是有数据类型的,去掉数组名就是数组的类型了,比如int  arr[2][4]的类型是int [2][4]。详细请看《一维、二维数组的基础知识超详细总结》

下标操作符[ ]失效:而在函数参数表中,下标操作符失去了确定数组类型的作用。但它起到了新的作用:下标操作符的个数反映着形式指针的级数

补充:因为接收变量是个形式指针,而不是数组。所以你在函数定义时,在“伪数组”下标写上数字和不写数字是一样

其实刚刚的例子除了arr的值与&arr的值不同可以看出来arr是指针,其实还有另一种看法:

伪数组arr的类型是 int* 型,这是个标准的指针类型;而原数组a的类型是 int[5] 型,这是个标准的数组类型。所以可以看出,arr并不是一个形式数组,而是一个形式指针。

#3. 二维数组传参不能省略列

数组传地址给函数后,函数只知道这是一片连续空间,但不知道你有没有对这块连续空间进行特殊的规划。又由于内存寻址公式的存在,你必须写下列数,函数才知道这块连续空间是怎么划分。

如果你把函数声明(或定义)写成:int  arr_print( int arr[ ][ ],int row,int col )。这样是不够的,此时函数只知道用1个二级指针来管理这块连续的内存空间,但并不知道要怎么划分。

补充:函数必须通过列数才能知道划分方法,与一维数组传参一样,接收变量的类型是指针类型,所以行下标写不写上都一样

六、嵌套调用与链式访问

嵌套调用:函数内部调用另一个函数。

(补充,递归调用是函数内部调用自己)

假设我们计算某年某⽉有多少天?我们可以设计2个函数来实现:

  1. is_leapyear():根据年份确定是否是闰年 
  2. days():调⽤函数is_leapyear确定是否是闰年后,再根据月份计算这个月的天数
int is_leapyear(int year)
{if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))return 1;elsereturn 0;
}
int days(int year, int month)
{int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int day = days[month];if (is_leapyear(year) && month == 2)	//这里嵌套调用了is_leapyear()day += 1;return day;
}

链式访问:将⼀个函数的返回值作为另外⼀个函数的参数

比如“ printf( "%zd\n", strlen("abcdef") ) ”,这里函数strlen的返回值也是函数printf的参数,像链条⼀样将函数串连起来。

七、函数的声明与定义

#1. 声明与定义的区别 和 注意事项

格式上:函数声明与函数定义相比,去除了大括号以及其中的内容,在函数头的末尾加上了分号。

这是函数定义:
int add(int a, int b)
{ return a+b; }这是函数声明:
int add (int a, int b);

1. 单个文件中:

建议:函数被调用前一定要有声明,有定义也可以,因为函数定义算是一种特殊的函数声明

2. 多个文件中:

建议:为了有条理,建议把函数声明、函数定义和主函数测试分开放在3个文件里。分成一个.h文件和两个.c文件,(1)自定义头文件.h存放自定义函数的声明以及库函数的头文件,(2)其中一个.c文件存放函数定义,(3)另一个.c文件存放main函数测试用例函数

注意:两个.c文件要包含自定义头文件.h

例如:

#2. 未声明的函数 

对于未声明的函数,编译器会默认函数的返回值是int型

用代码举例,此时两个.c文件都没有包含函数声明的头文件:

我们把鼠标放在函数名上,就能显示函数的类型:

我们可以发现,如果没有头文件声明,那么函数默认返回类型是int型。

当我们点击生成解决方案,代码是通过了检测的:

其实准确来说,失去了头文件关于该函数的声明,代码是失去了对该函数的格式检查的。

#3. 头文件声明的底层逻辑 和 函数调用操作符()

1.头文件的本质:

头文件本身不会参与链接,它们只是包含了一些函数声明、宏定义和类型定义等信息。编译器会将这些信息整合到目标文件中,然后链接器会将多个目标文件以及库文件链接在一起,生成最终的可执行文件或库文件。所以,头文件的作用是提供代码的结构和依赖关系信息

头文件提供的依赖关系信息,使得编译器在编译前可以检查函数格式是否正确。对于函数的检测,检查的东西包括(1)函数返回类型、(2)参数的个数、(3)每个参数的数据类型

2. 无头文件时的底层逻辑:

没有头文件声明时,编译器会把这些东西看作外部函数,在其他地方有定义。这时候编译器会使用默认的函数声明,即“ int  函数名 (void) ”

编译器使用默认函数声明后,系统不再对使用的函数格式进行检查,链接器会把这两个.c文件链接成一个.exe文件,然后交给CPU运行。

因为后续系统不再做检查,所以使用函数时即使输入的参数类型不符参数个数不符,这些问题都会被忽略

代码举例:

test.c文件:main函数测试

#include<stdio.h>
int main()
{int a[5] = { 1,2,3,4,5 };int c = arr_print(a, 5, "dad");  这里还多输入了一个无关参数“dad”printf("\n函数返回值:%d\n", c);     用c接收默认返回类型(int型)的值return 0;
}

function.c文件:数组打印函数

void arr_print(int arr[], int n)
{for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

运行结果:

现在是默认声明“int  arr_ptint(void)”,因为参数表是void的,系统不管你写不写。但既然我们写进了参数,它还是会按照正常的参数表顺序读取参数的。这里我们多了一个参数“dad”,因为没有第3个接收变量,所以并没有被函数读入。

如果我们补齐头文件的声明,那么还没编译就会报错:

这里指的是,明明声明函数的返回类型是void,你却用一个整型变量c来接收非法返回值。

为什么程序还能找到函数体并运行函数?因为有函数调用操作符()。

多个文件被链接器链接成一个文件后,函数调用操作符()可以解析函数名并找到函数体的地址


本期的内容到这里就结束了。函数的知识真的是多而杂,写得我真的是太难受了,还请您多多支持

Thanks♪(・ω・)ノ

相关文章:

函数(子程序)的常见、易混淆概念详解【对初学者有帮助】

C语⾔中的函数也被称做子程序&#xff0c;意思就是⼀个完成某项特定的任务的⼀小段代码。 C语⾔标准中提供了许多库函数&#xff0c;点击下面的链接可以查看c语言的库函数和头文件。 C/C官⽅的链接&#xff1a;https://zh.cppreference.com/w/c/header 目录 一、函数头与函…...

TiDB-从0到1-DM工具

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇TiDB-从0到1-配置篇TiDB-从0到1-集群扩缩容TiDB-从0到1-数据导出导入TiDB-从0到1-BR工具 一、DM原理 支持全量抽取数据\检测新的数据变化同步到下游实例…...

AppScan——Web 应用安全扫描的得力工具

一、引言 在当今数字化时代&#xff0c;Web 应用成为企业业务的重要支撑&#xff0c;但同时也面临着各种安全威胁。AppScan 作为一款专业的 Web 应用安全扫描工具&#xff0c;为保障 Web 应用的安全性提供了有力的支持。本文将对 AppScan 进行详细介绍&#xff0c;包括其功能、…...

虚幻5|AI行为树,进阶篇

一&#xff0c;打开敌人的角色蓝图&#xff0c;编写以下蓝图&#xff0c;该蓝图只是创建一个敌人并非ai行为树 1.编写蓝图 2.打开主界面&#xff0c;创建一个导航网格体积&#xff0c;上一章都有讲&#xff0c;在添加体积这里面&#xff0c;找到导航网格体积&#xff0c;点击创…...

在 Spring Boot 中配置 Tomcat 监听多个端口

在现代微服务架构中&#xff0c;应用程序可能需要监听多个端口&#xff0c;以支持不同的服务或协议。Spring Boot 提供了灵活的配置选项&#xff0c;使得这一需求变得简单而高效。本文将介绍如何在 Spring Boot 中配置 Tomcat 以监听多个端口&#xff0c;并简要说明其中一些关键…...

stm32f407新建项目工程及烧录

1、新建一个文件夹&#xff0c;打开keil5将项目工程放入文件夹中 2、弹出选择对应型号设备 3、弹出选择对应库 可以看见出现下图&#xff1a;感叹号表示有错 最后如图所示&#xff1a;点击ok就行了 4、创建对应的文件夹存放文件 4、建立main.c 5、添加对应的设置 最后写一个空白…...

c++中加不加const的值传递和引用传递的区别

文章目录 可以修改参数值的比较值传递(int x)和引用传递(int &x)使用const不修改参数值的比较值传递(const int x)和引用传递(const int &x)1. const int x 示例2. const int &x 示例 可以修改参数值的比较值传递(int x)和引用传递(int &x) #include <iost…...

Qt的窗口设置

本文介绍Qt的窗口设置。 采用Qt开发界面程序&#xff0c;会涉及到窗口的设置&#xff0c;如窗口标题栏是否显示&#xff0c;是否有最小&#xff0c;最大化按钮等&#xff0c;窗口当前显示最小化&#xff0c;最大化等。本文简要介绍常用的窗口设置方法。 1.窗口属性 窗口属性…...

51单片机-LCD1602显示屏

简介 是一个液晶显示屏&#xff0c;通过电压对显示区域进行控制&#xff0c;有电就显示。 能够同时显示32个字符&#xff0c;分为两行&#xff0c;一行显示16个字符。可以显示的内容只能是字母、数字或者一些特殊符号。 使用ASCII码来让LCD1602来显示对应的字符。 电路图 …...

多模态分析代理 MAIA:多智能体解决 视觉模型 黑盒问题

多模态分析代理 MAIA&#xff1a;多智能体解决 视觉模型 黑盒问题 论文&#xff1a;https://arxiv.org/pdf/2404.14394 代码&#xff1a;https://github.com/multimodal-interpretability/maia 提出背景 神经网络方法提取的特征&#xff0c;没有可解释性。 数据在通过多个层…...

AT360-6T杭州中科微单频高精度授时模块场景应用

AT360-6T是一款高性能多系统卫星定位授时模块&#xff0c;基于自主研发的北斗多系统SOC芯片&#xff0c;可以同时接收中国的BDS(北斗二号和北斗三号)、美国的GPS、俄罗斯的GLONASS、欧盟的 GALILEO 和日本的QZSS等多个卫星导航系统的GNSS信号来实现多系统联合定位授时&#xff…...

Python酷库之旅-第三方库Pandas(081)

目录 一、用法精讲 336、pandas.Series.str.rpartition方法 336-1、语法 336-2、参数 336-3、功能 336-4、返回值 336-5、说明 336-6、用法 336-6-1、数据准备 336-6-2、代码示例 336-6-3、结果输出 337、pandas.Series.str.slice方法 337-1、语法 337-2、参数 …...

C语言基础⑩——构造类型(结构体)

一、数据类型分类 1、基本类型 整数型 短整型&#xff1a;short&#xff08;2个字节&#xff09;&#xff1b;整型&#xff08;默认&#xff09;&#xff1a;int&#xff08;4个字节&#xff09;&#xff1b;长整型&#xff1a;long&#xff08;8个字节&#xff09;&#xf…...

宝兰德荣获openEuler项目群青铜捐赠人称号,共筑开源生态繁荣新篇章

近日&#xff0c;开放原子开源基金会正式公布了新增捐赠人名单&#xff0c;宝兰德凭借在开源领域的卓越贡献与深厚实力&#xff0c;被授予openEuler项目群青铜捐赠人称号。 开放原子开源基金会是致力于推动全球开源事业发展的非营利机构&#xff0c;于2020年6月在北京成立。开放…...

【Python单元测试】学习笔记3

文章目录 08.PyTest框架什么是PyTestPyTest的优点PyTest的测试环境PyTest常用参数跳过测试 09.PyTest fixture基础PyTest fixture定义和使用引用多个Fixture 10. conftest.pyconftest.py的用途 11. 参数化测试用例为什么需要参数化测试用例使用parameterizer插件实现使用pytest…...

OpenSSL源码编译及Debug

** 1. 环境 Linux 5.19.0-14-generic 22.04.1-Ubuntu 2. 所需工具 gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) cmake version 3.22.1 3. 步骤 3.1 获取openssl源码 方法可以git clone获得源码&#xff0c;或者直接去GitHub上下载压缩包&#xff0c;GitHub网址&#xf…...

go之goburrow/modbus 学习

goburrow/modbus 是一个用Go语言实现的Modbus协议库&#xff0c;提供了Modbus主机&#xff08;Master&#xff09;和从机&#xff08;Slave&#xff09;的实现&#xff0c;支持两种主要的Modbus传输模式&#xff1a;Modbus TCP和Modbus RTU。 功能介绍 1. 支持的传输模式 Mod…...

开放词汇目标检测(Open-Vocabulary Object Detection, OVOD)算法是什么?

开放词汇目标检测&#xff08;Open-Vocabulary Object Detection, OVOD&#xff09;算法是什么&#xff1f; 随着计算机视觉技术的快速发展&#xff0c;目标检测&#xff08;Object Detection&#xff09;已经在各种应用场景中得到了广泛的应用。然而&#xff0c;传统的目标检…...

【教程】Ubuntu给pycharm添加侧边栏快捷方式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 以下教程不仅限于pycharm&#xff0c;其他软件也是一样操作 1、进入到pycharm的目录&#xff0c;先通过命令行打开pycharm&#xff1a; ./bin/pycharm…...

三个月外贸小白好迷茫,该何去何从?

最近看到一个共性的问题&#xff0c;也许对于大多数外贸新人来说&#xff0c;都有过这样的困扰和无力感&#xff0c;也许对于每一个没有强大背景的外贸小伙伴来说&#xff0c;可能都是这样一路成长起来的。 大家好&#xff0c;我是一名普通二本英专生&#xff0c;八月中旬入职…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

全面解析各类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&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...