C语言----预处理(详解)

        好了书接上回。我在讲编译与链接的时候写过宏和条件建议。我说会在下一篇博客中讲解,那么来了。今天我们来详细的讲讲预处理。宏与条件编译也在其中,那么我们现在就来好好会会这个预处理吧。

预定义符号

        关于预定义符号,我暂时只知道几个。并且我知道的这几个对于我们现在还在学习阶段感觉没什么帮助,哈哈。我知道的这几个预定义符号应该对于我们以后工作时写日志,应该是有帮助吧。这个有什么作用还是等大家日后自己定夺吧,这里我就先给大家引出来。

__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

86254f5101e34ee2905c53b5e98a6fe8.png

       这里我是先进行执行后才将__STDC__给打出来的,不是在执行前就写出来的,大家注意一样,别误会了。然后大家可以看一下我打印出来的结果。__FILE__:详细的打印出来被编译文件的地址。__LINE__:打印了这条代码是在第几行。虽然我自己在编译器中没有标注出来,但我觉得编译器应该不会错吧,1要是真的出错我更愿意相信是自己写代码错误了。__DATE__ :打印日期,但是大家要注意顺序是几月份(英语),几号,多少年。这个顺序与我们平常的顺序是有一点不一样的,但是一下子也可以看出来,大家稍微注意一下就可以了。__TIME__:打印时间这个就没什么好说的了,一下子就可以看出来了。最后的__STDC__:因为我的编译器没有遵循ANSI C,所以就是未定义。我觉得应该是vs都是这样的吧,大家可以尝试其他的编译器看看,反正在下的编译器是没办法的了。

int main()
{
	printf("file:%s line:%d\n", __FILE__, __LINE__);
	printf("date:%s time:%s\n", __DATE__, __TIME__);
	printf("stdc:%d\n", __STDC__);
	return 0;
}

#define

       我想写过扫雷代码的朋友应该都使用过这个吧,至少大家应该都看过这个吧。#define定义的标识符常量和宏都会在在预处理阶段,对程序中所有出现的标识符,宏名都会被定义的内容替换掉。大家可以理解写这个是是我们写宏的前提。

#define name stuff

        上面只是一个基本格式,那么如何使用,我们来看看下面的代码吧:

ba31bcceb0694da3a7f59b5d83ce471b.png

         这里我只是定义了一个为100的常量和一个符号while(1),不知道大家有没有看出这个#define的作用,我们只需要在最开头的地方写出#define,然后空一个写名字,再空一个写内容。这样一个简单的定义就完成了,那是不是只能写像我上面的那些简单的定义嘞,当然不是啦。还有一些定义比如:

#define DEBUG_PRINT printf("file:%s\tline:%d\t \
 date:%s\ttime:%s\n" ,\
 __FILE__,__LINE__ , \
 __DATE__,__TIME__ )

#define CASE break;case //在写case语句的时候⾃动把 break写上。

      不知道大家是否注意到我上面的这个代码是否有一个\,那么这个\是干什么的嘞,其实这个就是方便我们观看,我们换行了。如果我不写\的话那这个代码就是这样比较长,也许这样可能还还算可以看,但是要还一个更多的代码的话,怎么搞,是吧。这样有一个\,不仅方便我们观看而且还便于我们写的时候看。

153886e3b76a427e9febe3ad130c745d.png

        然后这里再提一嘴,我们要不要在#define定义标识符后面是否需要写;嘞?其实这个写了也无所谓的,编译器并不会报错,但是这对我们的代码会有一点的影响,大家可以看看:

282ffc3d9e0241fd9da9bd302c437369.png

        大家看到这个,我们知道我们这里#define并没有报错,但是当我们使用的时候却报错了。所以在#define定义标识符后面写;并不会报错,但是当我们使用的时候需要注意,不然会出现我们意想不到的问题。所以我们建议不在#define定义后面写;。不然的话可能会造成意想不到的问题。

#define定义宏

        好了,终于到了我们上面一篇博客中提及过的知识了。关于宏的知识了。允许把参数替换到⽂本中,这种实现通常称为宏(macro)或定义宏 (define macro)。参数列表的括号必须与我们写的名字紧凑,不能分开,要是分开了的话,那就是另外一个东西了。

#define name( parament-list ) stuff

        那么我们写点代码来看看吧:

deaa3db44539440584eb946b3ea7de01.png

        在这里我写了一个wu和yh一个是判断大小一个是平方。这里就是运用了把参数替换到⽂本中的,大家可以在看一下替换后的样子是什么样的:

5f21fefee6f34469a66749a7dfe70e18.png

       好,我们看到这里大家应该看到了如何定义宏了吧。但是大家想一下下面的这个代码结果是什么嘞:

b79578c8635b43b58b420916f9a832fd.png

       大家看一下我们打印的第三个结果是多少,是11。但是我们看我们想要的结果是多少啊。我们应该想要的是36啊。怎么会是11啊。所以着我们就得考虑考虑运算符的优先级了呀。我们这里替换的话是什么样子的话:

2ef5a8123c6a43049ef979b305952979.png

       这样打大家看一下,是吧,这样替换的话,得到11的话好像也是正确的呀,但是对于我们想要的结果却是错误的啊,那么我们如何想要得到我们的结果嘞。

c49de5189c4a465898c2dc46e2c032d6.png

       是不是,我们在宏的时候加了括号后,结果就舒服了。是吧那我们再看看下面的代码结果又是什么样子的嘞:

f6e8258680894af6bd3373f2b5f67ed4.png

      我们第5个结果是55,但是大家看一下,我们写这个代码是想得到的结果是个啥嘞。我们是不是像10*10结果为100呀。但为什么我们结果是55嘞。那就是我们

2e08b41aafb449aaa4b42614b04a6a09.png

       这可以看出我们给传递参数的时候是没问题的,但是我们其实是想在传递后将计算结果后再乘以10那怎么搞嘞:

1382dbbab4414bbfa0246bd822e5d70e.png

          是吧,这不就迎刃而解了呀我们在传递过后计算后再计算其他的。经过上面这两个坑后,是不是觉得哎呀,这几个问题都是括号起来关键作用啊。没错,我们在定义宏的时候不要舍不得括号,是吧。我们这里开始就是因为没写括号所以出现了问题。

#define xi 100;
#define ha while(1)
#define wu(y,x) x>y?x:y 
#define yh(x) (x)*(x)
#define yh1(x) ((x)+(x))
//#define DEBUG_PRINT printf("file:%s\tline:%d\t date:%s\ttime:%s\n" ,__FILE__,__LINE__ ,  __DATE__,__TIME__ )

int main()
{
	int h = wu(3, 6);//int h=3>6?3:y;
	printf("%d\n", h);
	int xix = yh(6);//int xix=6*6;
	printf("%d\n", xix);
	printf("%d\n", yh(5+1));//5+1*5+1=5+5+1=11
	printf("%d\n", yh(5 + 1));
	printf("%d\n",10* yh1(5));//10*(5+5)
	//ha
	//{
	// printf("%d\n",xi);
	//	
	//}
	return 0;
}

宏定义的副作用

       大家都知道是药三分毒,既然救人的要都是有毒性的,那么我们宏有点副作用一个很正常吧。当宏参数在宏的定义中出现超过⼀次的时候,如果参数带有副作⽤,那么你在使⽤这个宏的时候就可能出现危险,导致不可预测的后果。副作⽤就是表达式求值的时候出现的永久性效果。那具体是个什么副作用嘞,我来写一个例子大家看一下:
cf59b5beef3542a8aa916c90abfded8d.png

   

        大家可以看一下,明明我传的是5++和8++。再怎么说吧,也不会搞个10出来吧,这是为什么嘞。当然出现问题先找自己的原因。编译器肯定是没用错的。想想如果我们把参数替换掉会是什么样子的嘞。6ef366c49ef140b5aacdb1434432e1d8.png

        大家看一下,我们要是替换过后结果是不是这样的,我们先替换过去后,都++了。然后当判断结束后,要返回值的时候又++了,那么最大的值是不是有两个++了。这也就表明了宏并不是无所不能的,在一定方面上看宏的使用是不如自定义函数的。我们当我们学会了使用宏定义后不要什么都是宏定义,大家要思考一下使用宏定义一点还是自定义函数。

#define na(x,y) ( (x) > (y) ? (x) : (y) )

int main()
{
	int x = 5;
	int y = 8;
	int z = na(x++, y++);//((5++)>(8++)?(5++):(8++))
	printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
	return 0;
}

宏替换的规则

在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。
1. 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

       上面这里表明了,宏可以出现其他的#define但是不要在定义的时候就加入其他的#define不然是会像下面一样报错的。

#define na(x,y) ( (x) > (y) ? (x) : (y) )
#define xi(x) (x++)
#define ha(x,y) ( (xi(x)) > xi((y)) ? ((x)) : ((y)) )
int main()
{
	int a = 5;
	int b = 7;
	int m = ha(a, xi(b));
	printf("%d", m);
//	int x = 5;
//	int y = 8;
//	int z = na(x++, y++);//((5++)>(8++)?(5++):(8++))
//	printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
	return 0;
}

宏函数的对比

       不知道大家是否还记得我在上面提醒过大家不要学习了宏定义后就只用宏定义了。不要忘了我们还有自定义函数呀。这里我们就来简单的概述一下,宏与自定义函数的优缺点吧。

        首先有一个致命的问题就是宏不能执行太多代码的问题,尽管我们可以使用\来换行,但是大家要记得,宏的替换规则是直接替换呀,我先替换过后,再一步一步计算。但自定义函数就不一样了,我们平常大量代码都是这样写的所以大家在使用宏定义的时候可要好好斟酌啊。

       接着宏是无法直接调试的,虽然我又方法让我们看到预编译的结果,但是我们在vs上面是无法看到的呀,我们只能看到编译后的结果。这就让我们一点难受了。要是写代码出问题了,我调试都无法直接找到问题。还得我们一行一行的找问题。

     然后就是我们开头讲过的优先级问题了。当时我们说过,不要吝啬括号。如果我们一不小心少写了个括号,那么这个结果也是天差地别的了。

      再来就是每次使⽤宏的时候,⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短,否则可能⼤幅度增加程序的长度。也就是说我们每次使用宏定义都是直接替换的,如果宏定义太长的话,替换过去,那这个代码也就太长了。

      然后说一个像有点但又像缺点的点吧。就是宏定义不吃数据类型的问题。就是因为不吃类型所以我们只管写就可以了,但是因为不管类型,那么我们写什么都可以,是不是就不太严谨啊。是吧。而且宏的参数可以出现类型,但是函数做不到。:

#define ha(x) sizeof(x)

int main()
{

	printf("%d", ha(int));
	return 0;
}

       但宏并不是一无是处不然也不会有这个东西了是吧。⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐ 函数在程序的规模和速度⽅⾯更胜⼀筹。

#与##

#

      #运算符将宏的⼀个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。
#运算符所执⾏的操作可以理解为”字符串化 “。 也就是说当我们像要在#define里面打印字符串面量的话用#就对了。还是来看看吧:

       我们这里是想要打印的结果是x is 6.但是结果却是a is 6这就是差别嘛。如果想要打印的字符串是我们传递的那个面量的话,#就派上用场了。

        是吧,这里我们就讲打印的变为了传递过来的字符串面量。

#define yh(a) printf("a is %d\n",a)
#define hld(a) printf(""#a" is %d",a)

int main()
{
	int x = 6;
	yh(x);
	int c = 9;
	hld(c);
	return 0;
}

##

      ## 可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的⽂本⽚段创建标识符。 ## 被称为记号粘合这样的连接必须产⽣⼀个合法的标识符。否则其结果就是未定义的。但是关于##的知识鄙人也是了解较少。因为平常生活中很少看见,也很少使用所以我就找了一个差不多的代码给大家展示:

        写⼀个函数求2个数的较⼤值的时候,不同的数据类型就得写不同的函数。日常情况下,我们是会写出不同类型的对比方式的,如:下面的那个便是使用了关于##的知识了,因为鄙人对这个的了解也是很少,所以大家看一下,有个了解吧。
int int_max(int x, int y)
{
 return x>y?x:y;
}
float float_max(float x, float y)
{
 return x>yx:y;
}

条件编译

        这里又提及到我上一篇博客写过的知识了。条件编译。那什么是条件编译嘞在编译⼀个程序的时候我们如果要将⼀条语句(⼀组语句)编译或者放弃是很⽅便的。因为我们有条件编译指令。那么我们还是来看看代码吧:

        是吧,大家可以看一下这个是看是否定义了宏,然后干什么的。当然还有其他的比如说:

#if 常量表达式
 //...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
 //..
#endif
2.多个分⽀的条件编译
#if 常量表达式
 //...
#elif 常量表达式
 //...
#else
 //...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif

         这里就与我们平常使用的条件判断一样,只是这里是关于宏的。

命名约定

        好了上面的几乎就是这篇博客中的只要成分了,接下里的是一些相对于上面算补充的知识了。

       ⼀般来讲函数的宏的使⽤语法很相似。所以语⾔本⾝没法帮我们区分⼆者。
       那我们平时的⼀个习惯是:
把宏名全部⼤写
函数名不要全部⼤写

#undef

        这是移除一个宏定义,直接演示一下:因为开始我没有移除ha所以可以打印出一个,但是当我移除后,就没办法打印第二个了,这个比较简单大家应该都很容易就理解了。

#define ha
int main()
{
//#ifdef ha//如果定义了ha就打印
//	printf("hahah");
//#else//没定义的话就打印这个
//	printf("xixix");
//#endif
	#ifdef ha//如果定义了ha就打印
	printf("hahah");
#endif
#undef ha
#ifdef ha//如果定义了ha就打印
	printf("hahah");
#endif
	return 0;
}

命令行定义

        许多C 的编译器提供了⼀种能⼒,允许在命令⾏中定义符号。⽤于启动编译过程。例如:当我们根据同⼀个源⽂件要编译出⼀个程序的不同版本的时候,这个特性有点⽤处。(假定某个程序中声明了⼀个某个⻓度的数组,如果机器内存有限,我们需要⼀个很⼩的数组,但是另外⼀个机器内存⼤些,我们需要⼀个数组能够⼤些。)就是说我们可以先写代码,大小的事情,等我写完了之后。看要多少,我再改。但是很遗憾vs没法演示出来,鄙人只能搞个代码出来,希望大家可以在自己的电脑上上尝试一下,然后再评论区里面贴个照片,也便鄙人学习学习。

#include <stdio.h>
int main()
{
 int array [ARRAY_SIZE];
 int i = 0;
 for(i = 0; i< ARRAY_SIZE; i ++)
 {
 array[i] = i;
 }
 for(i = 0; i< ARRAY_SIZE; i ++)
 {
 printf("%d " ,array[i]);
 }
 printf("\n" );
 return 0;
}
//linux 环境演⽰
gcc -D ARRAY_SIZE=10 programe.c

头文件包含

      关于头文件的包含也算一个拓展知识了吧。我们知道包含头文件可以是

       我们知道<>是我们包含系统自带的,然后" "是包含我们自己写。但是我们也可以用" " 来写系统自带的文件。

       但是这样做查找的效率就低些,当然这样也不容易区分是库⽂件还是本地⽂件了。系统会在这个文件找一遍,然后去系统里面找,找到了就找到了,没找到的话就报错了。如果是正确的话是没什么问题的,这样也会加大系统运行效率呀是吧。虽然我们可以这样使用,但还是建议大家正常使用头文件的包含符号。

        好了,以上就是鄙人想与大家分享的预处理详解了。由于能力不足,还有很多地方是欠缺的,希望大家包含,并且可以在评论区里面指出。

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

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

相关文章

Python-VBA编程500例-028(入门级)

经典二分查找算法(Classic Binary Search Algorithm)(也称为折半查找算法)&#xff0c;是一种在有序数组中查找某一特定元素的搜索算法。它要求序列必须有序&#xff0c;然后通过每次比较数组中间元素与目标值&#xff0c;将搜索范围缩小一半&#xff0c;直到找到目标元素或搜索…

传输大咖20|提升效率:优化文件服务器删除大文件夹过程的策略

引文&#xff5c; 文件服务器在删除大文件夹时&#xff0c;往往会比较耗时。如果在原有线程中同步等待删除结果&#xff0c;那么会阻塞原有线程的运行&#xff1b;如果在其它线程中异步删除文件夹&#xff0c;则虽不阻塞原有线程的运行&#xff0c;但对于那些关注删除结果的用户…

每日面经分享(pytest装饰器)

pytest装饰器 a. pytest.mark.parametrize&#xff1a;这个装饰器用于标记测试函数&#xff0c;并为其提供多组参数进行参数化测试。可以使用元组、列表、字典等形式来指定参数组合。 import pytestpytest.mark.parametrize("num1, num2, expected", [(2, 2, 4), (5…

力扣热门算法题 217. 存在重复元素,223. 矩形面积,225. 用队列实现栈

217. 存在重复元素&#xff0c;223. 矩形面积&#xff0c;225. 用队列实现栈&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.04.01 可通过leetcode所有测试用例。 目录 217. 存在重复元素 解题思路 完整代码 Java Python 223…

Discuz采集发布插件

Discuz&#xff08;简称DZ&#xff09;是一款知名的开源论坛系统&#xff0c;广泛应用于各类网站社区。对于许多站长来说&#xff0c;保持论坛内容的更新是一项挑战&#xff0c;特别是在内容量庞大的情况下。为了解决这个问题&#xff0c;有一类特殊的插件是用于在Discuz论坛中…

惟客数据《2024泛零售行业大会员经营实践与案例》正式发布

对于多业态、多品牌、多渠道经营的泛零售企业而言&#xff0c;如何改变过去会员经营过程中“各自为政”的状态&#xff1f; 如何让企业内不同业务之间的会员经营“瞄得准、看得穿、打得透、流得通、触得动”&#xff0c;充分发挥多业态、多品牌、多渠道优势&#xff0c;最大化挖…

transformers微调模型后使用pieline调用无法预测列表文本

初学transformers框架 使用trainer简单训练一个文本分类模型三个epoch后 使用piepline调用model 和tokenizer后 发现 传入列表文本后 输出就变得不正常了&#xff0c;为么子哇 如下图

简单说清楚什么是SQL Injection?

最近看完了《The Pragmatic Programmer: 20th Anniversary Edition, 2nd Edition: Your Journey to Mastery》&#xff0c;在第7章&#xff1a;While You Are Coding的footnotes中&#xff0c;提到了一幅漫画&#xff1a; 这不仅用简单的方式说清楚了什么是SQL Injection&#…

【御控物联】JSON结构数据转换在物联业务中应用(场景案例二)

文章目录 一、物联网业务场景现状二、物联网业务场景数据交互格式三、JSON格式数据转换案例四、JSON数据格式转换DEMO 一、物联网业务场景现状 目前&#xff0c;市场上多数物联网关与物联平台捆绑售卖&#xff0c;网关采集到设备数据只能按照指定的协议和规定的数据格式传输到…

蚂蚁测试可控制天气的“龙王”系统

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 所有伟大的发明&#xff0c;都来自最初不切实际的幻想。 4月1日&#xff0c;不少互联网大厂都发布一些新产品&#xff0c;例如&#xff1a;淘宝测试用火箭送快递&#xff0c;蚂蚁集团推出可以控制天气的技术系统畅…

【学习笔记】java项目—苍穹外卖day03

文章目录 苍穹外卖-day03课程内容1. 公共字段自动填充1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3 步骤三 1.4 功能测试1.5 代码提交 2. 新增菜品2.1 需求分析与设计2.1.1 产品原型2.1.2 接口设计2.1.3 表设计 2.2 代码开发2.2.1 文件上传实现2.2.2 新…

MySQL 进阶-----索引使用规则

目录 前言 一、验证索引效率 二、最左前缀法则 三、范围查询 四、索引失效情况 1.索引列运算 2.字符串不加引号 3 .模糊查询 4.or连接条件 5 .数据分布影响 前言 本期我们学习MySQL索引的使用方法&#xff0c;在讲解索引的使用原则之前&#xff0c;先通过一个简单的…

【漏洞复现】通天星CMSV6弱口令漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

探索 Redis 数据库:一款高性能的内存键值存储系统

目录 引言 一、非关系型数据库 &#xff08;一&#xff09;什么是非关系型数据库 &#xff08;二&#xff09;非关系型数据库的主要特征 &#xff08;三&#xff09;关系数据库与非关系型数据库的区别 二、Redis 简介 &#xff08;一&#xff09;基本信息 &#xff08;…

哪一款个微管理助手比较好用?

私域流量兴起&#xff0c;社群运营成为热门&#xff0c;越来越多的行业进入社群的圈子。但是社群管理是个超麻烦、巨琐碎的活儿&#xff0c;方法不对&#xff0c;很容易无限陷入死循环。 此时&#xff0c;一个合适的管理工具可以帮我们高效管理&#xff0c;达到事半功倍的效果…

构建第一个ArkTS应用(Stage模型)

创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。选择Application应用开发&#xff08;本文以应用开发为例&#xff0c;Atomic Servi…

云原生最佳实践系列 7:基于 OSS Object FC 实现非结构化文件实时处理

方案概述 现在绝大多数客户都有很多非结构化的数据存在 OSS 中&#xff0c;以图片&#xff0c;视频&#xff0c;音频居多。举一个图片处理的场景&#xff0c;现在各种终端种类繁多&#xff0c;不同的终端对图片的格式、分辨率要求也不同&#xff0c;所以一张图片往往会有很多张…

泰迪智能科技高职人工智能专业人才培养方案

人工智能专业坚持以立德树人为根本&#xff0c;立足社会经济发展&#xff0c;面向信息技术行业&#xff0c;培养德智体美劳全面发展的人工智能领域的高素质工程型专门人才。毕业生具备扎实的数学、自然科学、工程技术、人文社科的基本理论, 系统深入的人工智能专业知识和实践能…

云原生最佳实践系列 6:MSE 云原生网关使用 JWT 进行认证鉴权

方案概述 MSE 网关可以为后端服务提供转发路由能力&#xff0c;在此基础上&#xff0c;一些敏感的后端服务需要特定认证授权的用户才能够访问。MSE 云原生网关致力于提供给云上用户体系化的安全解决方案&#xff0c;其中 JWT 认证能力是在 Json Web Token 这种结构化令牌的基础…

递归究竟是什么?如何快速编写正确的递归代码? —— 力扣经典面试题详解

递归究竟是什么&#xff1f;如何快速编写正确的递归代码&#xff1f; —— 力扣经典面试题详解 一、递归1.1 什么是递归&#xff1f;1.2 为什么会用到递归&#xff1f;1.3 如何快速编写正确的递归代码&#xff1f; 二、力扣相关笔试题解析[面试题 08.06. 汉诺塔问题](https://l…