• Keil C51宏及宏函数的应用


           宏(Macro)本质上就是代码片段,通过别名来使用。可简单地将宏编程理解为:按一定规则将文本定义为一个标志符,在编程时直接引用标志符替代这些文本进行编程,在程序编译时,再将标志符替换回预定义的文本的过程。C语言预编译器主要实现宏替换功能。Keil C51与标准C语言大同小异,也有宏系统。

           宏定义格式如下:

           #define 标志符[(参数表)] 字符串

            注意字符串结尾处不可有分号。宏定义标准符后面可以带小括号,括号里面也可以带参数表,也可以不带参数。带小括号的宏定义类似于函数定义,我们可以把这种宏叫做宏函数。

          宏的主要应用如下:

    1. 定义别名,如下面代码:

    1. #ifndef BYTE
    2. #define BYTE unsigned char
    3. #endif
    4. #ifndef byte
    5. #define byte unsigned char
    6. #endif
    7. #ifndef ui8
    8. #define ui8 unsigned char
    9. #endif
    10. #ifndef UI8
    11. #define UI8 unsigned char
    12. #endif
    13. #ifndef U8
    14. #define U8 unsigned char
    15. #endif
    16. #ifndef u8
    17. #define u8 unsigned char
    18. #endif
    19. #ifndef uchar
    20. #define uchar unsigned char
    21. #endif
    22. #ifndef INT8
    23. #define INT8 signed char
    24. #endif
    25. #ifndef int8
    26. #define int8 signed char
    27. #endif
    28. #ifndef I8
    29. #define I8 signed char
    30. #endif
    31. #ifndef i8
    32. #define i8 signed char
    33. #endif

    2. 定义常数,如下面代码:

    #define FSCLK 30000000L

    3. 避免头文件重复包含的宏定义,如下面代码:

    1. /*config.h
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 06/22/2022
    5. */
    6. #ifndef __CONFIG_H__
    7. #define __CONFIG_H__
    8. #define FOSC 30000000UL
    9. //********************************************************
    10. void SysInit(); //init System speed fastest
    11. #endif

    4. 条件编译的宏定义,如下面代码:

    1. /*******************************************************************************
    2. * 函 数 名 : LcdInit()
    3. * 函数功能 : 初始化LCD屏
    4. * 输 入 : 无
    5. * 输 出 : 无
    6. *******************************************************************************/
    7. #define LCD1602_4PINS
    8. #ifndef LCD1602_4PINS
    9. void Lcd1602_Init() //LCD初始化子程序
    10. {
    11. LCDWriteCom(0x38); //开显示
    12. LCDWriteCom(0x0c); //开显示不显示光标
    13. LCDWriteCom(0x06); //写一个指针加1
    14. LCDWriteCom(0x01); //清屏
    15. LCDWriteCom(0x80); //设置数据指针起点
    16. }
    17. #else
    18. void LCDInit() //LCD初始化子程序
    19. {
    20. LCDWriteCom(0x32); //8位总线转为4位总线
    21. LCDWriteCom(0x28); //在四位线下的初始化
    22. LCDWriteCom(0x0c); //开显示不显示光标
    23. LCDWriteCom(0x06); //写一个指针加1
    24. LCDWriteCom(0x01); //清屏
    25. LCDWriteCom(0x80); //设置数据指针起点
    26. }
    27. #endif
    28. //*************************************************************************

    5. 取代函数定义,宏函数定义与函数定义有些相像,但是其编译执行过程是完全不一样的,宏函数在编译工程中是完全的代码替换,而函数则是编译成机器码仿真特定的地址空间内,调用时需要来回跳转,效率不如宏函数高,但是使用宏函数定义编译后的程序可能会占用更大的程序存储空间。

    宏函数定义及应用

    1. 无参数表宏函数定义  宏函数名实际就是后面宏函数体的别名,因此定义宏函数在宏函数名前不能有返回值类型,宏函数体实际上是以宏函数名的连续文本,在换行前必须加反斜杠(\),如下面代码示:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define InitP0_PPOut() {\
    9. P0M1 = 0x00;\
    10. P0M0 = 0xFF;\
    11. }
    12. void main()
    13. {
    14. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    15. P3 = 0xFF;
    16. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    17. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    18. InitP0_PPOut();
    19. SundBuzzerx10ms(50);
    20. Delay10xms(50, FSCLK);
    21. SundBuzzerx10ms(50);
    22. while(1)
    23. {
    24. UartS1_SendString("Uart S1 Test!");
    25. Delay10xms(200, FSCLK);
    26. }
    27. }
    28. //End of main()
    29. //*********************************************
    30. void SundBuzzerx10ms(ui8 x)
    31. {
    32. BUZZER = 0;
    33. Delay10xms(x, FSCLK);
    34. BUZZER = 1;
    35. }

    编译以上代码 ,结果如下:

     下面修改一下代码,多次引用宏函数,修改后的代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define InitP0_PPOut() {\
    9. P0M1 = 0x00;\
    10. P0M0 = 0xFF;\
    11. }
    12. void main()
    13. {
    14. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    15. P3 = 0xFF;
    16. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    17. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    18. InitP0_PPOut();
    19. InitP0_PPOut();
    20. InitP0_PPOut();
    21. InitP0_PPOut();
    22. InitP0_PPOut();
    23. SundBuzzerx10ms(50);
    24. Delay10xms(50, FSCLK);
    25. SundBuzzerx10ms(50);
    26. while(1)
    27. {
    28. UartS1_SendString("Uart S1 Test!");
    29. Delay10xms(200, FSCLK);
    30. }
    31. }
    32. //End of main()
    33. //*********************************************
    34. void SundBuzzerx10ms(ui8 x)
    35. {
    36. BUZZER = 0;
    37. Delay10xms(x, FSCLK);
    38. BUZZER = 1;
    39. }

    再编译,看下编译结果怎么样,结果如下:

    可以看出code由2533变成了2553, 现在把宏函数改为函数,并调用一次,改好的代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. void InitP0_PPOut() {
    9. P0M1 = 0x00;
    10. P0M0 = 0xFF;
    11. }
    12. void main()
    13. {
    14. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    15. P3 = 0xFF;
    16. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    17. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    18. InitP0_PPOut();
    19. //InitP0_PPOut();
    20. //InitP0_PPOut();
    21. //InitP0_PPOut();
    22. //InitP0_PPOut();
    23. SundBuzzerx10ms(50);
    24. Delay10xms(50, FSCLK);
    25. SundBuzzerx10ms(50);
    26. while(1)
    27. {
    28. UartS1_SendString("Uart S1 Test!");
    29. Delay10xms(200, FSCLK);
    30. }
    31. }
    32. //End of main()
    33. //*********************************************
    34. void SundBuzzerx10ms(ui8 x)
    35. {
    36. BUZZER = 0;
    37. Delay10xms(x, FSCLK);
    38. BUZZER = 1;
    39. }

    编译结果如下:

    现在去掉多次调用前的注释,多次调用函数,修改后的代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. void InitP0_PPOut() {
    9. P0M1 = 0x00;
    10. P0M0 = 0xFF;
    11. }
    12. void main()
    13. {
    14. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    15. P3 = 0xFF;
    16. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    17. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    18. InitP0_PPOut();
    19. InitP0_PPOut();
    20. InitP0_PPOut();
    21. InitP0_PPOut();
    22. InitP0_PPOut();
    23. SundBuzzerx10ms(50);
    24. Delay10xms(50, FSCLK);
    25. SundBuzzerx10ms(50);
    26. while(1)
    27. {
    28. UartS1_SendString("Uart S1 Test!");
    29. Delay10xms(200, FSCLK);
    30. }
    31. }
    32. //End of main()
    33. //*********************************************
    34. void SundBuzzerx10ms(ui8 x)
    35. {
    36. BUZZER = 0;
    37. Delay10xms(x, FSCLK);
    38. BUZZER = 1;
    39. }

     

          通过以上测试可以看出,如果调用一次,使用宏函数比使用函数编译后的代码所占空间更小,如果需要多次调用,函数调用编译后的代码更小。使用宏函数运行速度会更快,因为它没有了,函数间的跳转,这一点前面已经讲过。这样我们就明白了在何时使用宏函数,何时使用函数。

    2. 带参数表的宏函数定义  由于宏函数在编译时先由预处理器进行文本替换然后再编译,因此参数列表中的参数不需要,也不能有类型约束,另外规则规定参数展开需要加小括号,否则会得到不可预测的结果。下面是一个正确的宏函数定义:

    1. #define Sum(x, y) {\
    2. return (x) + (y);\
    3. }

    现将上面的宏函数放入程序中,看能否编译通过,程序代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y) {\
    9. return (x) + (y);\
    10. }
    11. int main()
    12. {
    13. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    14. P3 = 0xFF;
    15. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    16. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    17. InitP0_PPOut();
    18. InitP0_PPOut();
    19. InitP0_PPOut();
    20. InitP0_PPOut();
    21. InitP0_PPOut();
    22. SundBuzzerx10ms(50);
    23. Delay10xms(50, FSCLK);
    24. SundBuzzerx10ms(50);
    25. /*
    26. while(1)
    27. {
    28. UartS1_SendString("Uart S1 Test!");
    29. Delay10xms(200, FSCLK);
    30. }
    31. */
    32. Sum(8, 7);
    33. }
    34. //End of main()
    35. //*********************************************
    36. void SundBuzzerx10ms(ui8 x)
    37. {
    38. BUZZER = 0;
    39. Delay10xms(x, FSCLK);
    40. BUZZER = 1;
    41. }

    编译结果如下:

     编译通过。下面我们来试一下能否像函数那样调用,修改程序代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y) {\
    9. return (x) + (y);\
    10. }
    11. int a;
    12. int main()
    13. {
    14. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    15. P3 = 0xFF;
    16. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    17. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    18. SundBuzzerx10ms(50);
    19. Delay10xms(50, FSCLK);
    20. SundBuzzerx10ms(50);
    21. /*
    22. while(1)
    23. {
    24. UartS1_SendString("Uart S1 Test!");
    25. Delay10xms(200, FSCLK);
    26. }
    27. */
    28. a = Sum(8, 7);
    29. return a;
    30. }
    31. //End of main()
    32. //*********************************************
    33. void SundBuzzerx10ms(ui8 x)
    34. {
    35. BUZZER = 0;
    36. Delay10xms(x, FSCLK);
    37. BUZZER = 1;

    编译结果如下:

     编译不能通过。现在再修改一下宏函数定义及程序代码,修改后的程序如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y,z) {\
    9. z = (x) + (y);\
    10. }
    11. int a;
    12. int main()
    13. {
    14. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    15. P3 = 0xFF;
    16. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    17. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    18. SundBuzzerx10ms(50);
    19. Delay10xms(50, FSCLK);
    20. SundBuzzerx10ms(50);
    21. /*
    22. while(1)
    23. {
    24. UartS1_SendString("Uart S1 Test!");
    25. Delay10xms(200, FSCLK);
    26. }
    27. */
    28. Sum(8, 7, a);
    29. return a;
    30. }
    31. //End of main()
    32. //*********************************************
    33. void SundBuzzerx10ms(ui8 x)
    34. {
    35. BUZZER = 0;
    36. Delay10xms(x, FSCLK);
    37. BUZZER = 1;
    38. }

    编译结果如下:

     编译通过。上面宏函数得到的结果是否是正确的呢?下面来做下测试,先修改程序代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y,z) {\
    9. z = (x) + (y);\
    10. }
    11. ui8 mstr[20] = {0};
    12. int a = 0;
    13. void main()
    14. {
    15. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    16. P3 = 0xFF;
    17. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    18. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    19. SundBuzzerx10ms(50);
    20. Delay10xms(50, FSCLK);
    21. SundBuzzerx10ms(50);
    22. Sum(8, 7, a);
    23. while(1)
    24. {
    25. memset(mstr,0,strlen(mstr));
    26. sprintf(mstr,"a = %d\n",a);
    27. UartS1_SendString(mstr);
    28. Delay10xms(200, FSCLK);
    29. }
    30. }
    31. //End of main()
    32. //*********************************************
    33. void SundBuzzerx10ms(ui8 x)
    34. {
    35. BUZZER = 0;
    36. Delay10xms(x, FSCLK);
    37. BUZZER = 1;
    38. }

    编译结果如下: 

    编译通过,现在把它下载到单片机,看下结果如何

     

     上面是在串口助手中看到的结果,可以看出 a=15,结果是正确的。注意宏函数中z在展开时没有加括号。先修改一下程序代码,修改后如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y,z) {\
    9. z = (x) + (y);\
    10. }
    11. ui8 mstr[20] = {0};
    12. int a = 0;
    13. void main()
    14. {
    15. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    16. P3 = 0xFF;
    17. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    18. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    19. SundBuzzerx10ms(50);
    20. Delay10xms(50, FSCLK);
    21. SundBuzzerx10ms(50);
    22. while(1)
    23. {
    24. Sum(10, 15, a);
    25. memset(mstr,0,strlen(mstr));
    26. sprintf(mstr,"a = %d\n",a);
    27. UartS1_SendString(mstr);
    28. Delay10xms(200, FSCLK);
    29. }
    30. }
    31. //End of main()
    32. //*********************************************
    33. void SundBuzzerx10ms(ui8 x)
    34. {
    35. BUZZER = 0;
    36. Delay10xms(x, FSCLK);
    37. BUZZER = 1;
    38. }

    编译下载到单片机,结果如下:

     如果将程序修改如下,又会如何呢?修改后的程序代码如下:

    结果也是对的。尽管如此,在编程时还是建议在展开时将参数加上小括号,现将宏函数代码修改如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y,z) {\
    9. (x) = (x) + 1;
    10. (z) = (x) + (y);\
    11. }
    12. ui8 mstr[20] = {0};
    13. int a = 0;
    14. void main()
    15. {
    16. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    17. P3 = 0xFF;
    18. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    19. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    20. SundBuzzerx10ms(50);
    21. Delay10xms(50, FSCLK);
    22. SundBuzzerx10ms(50);
    23. while(1)
    24. {
    25. Sum(10, 1, a);
    26. memset(mstr,0,strlen(mstr));
    27. sprintf(mstr,"a = %d\n",a);
    28. UartS1_SendString(mstr);
    29. Delay10xms(200, FSCLK);
    30. }
    31. }
    32. //End of main()
    33. //*********************************************
    34. void SundBuzzerx10ms(ui8 x)
    35. {
    36. BUZZER = 0;
    37. Delay10xms(x, FSCLK);
    38. BUZZER = 1;
    39. }

    编译结果如下:

     不能通过编译。修改代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(x, y,z) {\
    9. (z) = (z) + 1;\
    10. (z) = (x) + (y);\
    11. }
    12. ui8 mstr[20] = {0};
    13. int a = 0;
    14. void main()
    15. {
    16. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    17. P3 = 0xFF;
    18. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    19. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    20. SundBuzzerx10ms(50);
    21. Delay10xms(50, FSCLK);
    22. SundBuzzerx10ms(50);
    23. while(1)
    24. {
    25. Sum(10, 1, a);
    26. memset(mstr,0,strlen(mstr));
    27. sprintf(mstr,"a = %d\n",a);
    28. UartS1_SendString(mstr);
    29. Delay10xms(200, FSCLK);
    30. }
    31. }
    32. //End of main()
    33. //*********************************************
    34. void SundBuzzerx10ms(ui8 x)
    35. {
    36. BUZZER = 0;
    37. Delay10xms(x, FSCLK);
    38. BUZZER = 1;
    39. }

    编译结果如下:

     再修改代码,修改后的代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. #define Sum(z, y,x) {\
    9. (x) = (x) + 1;\
    10. (x) = (z) + (y);\
    11. }
    12. ui8 mstr[20] = {0};
    13. int a = 0;
    14. void main()
    15. {
    16. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    17. P3 = 0xFF;
    18. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    19. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    20. SundBuzzerx10ms(50);
    21. Delay10xms(50, FSCLK);
    22. SundBuzzerx10ms(50);
    23. while(1)
    24. {
    25. Sum(10, 1, a);
    26. memset(mstr,0,strlen(mstr));
    27. sprintf(mstr,"a = %d\n",a);
    28. UartS1_SendString(mstr);
    29. Delay10xms(200, FSCLK);
    30. }
    31. }
    32. //End of main()
    33. //*********************************************
    34. void SundBuzzerx10ms(ui8 x)
    35. {
    36. BUZZER = 0;
    37. Delay10xms(x, FSCLK);
    38. BUZZER = 1;
    39. }

    再编译,看能否通过,编译结果如下:

    编译通过,能看出门道不?如果列表中有更多参数,有会怎样?参与的定义是否与后面的引用相关联?如果你对宏有正确认识,心里一定会有正确答案 。我们知道下面函数不能实现数据交换,该函数代码如下:

    1. void swap(int x, int y)
    2. {
    3. int tem = x;
    4. x = y;
    5. y = tem;
    6. }

    如果是宏函数呢?下面我们来试一下。程序代码如下:

    1. /*main.c
    2. Designed by Bill Liu
    3. Version 0.0
    4. Modified last by Bill Liu on 12/08/2021
    5. */
    6. #include "main.h"
    7. #include "stcint.h"
    8. ui8 mstr[20] = {0};
    9. int t1 = 10;
    10. int t2 = 15;
    11. int tem = 0;
    12. #define swap(x,y) {\
    13. tem =(x);\
    14. (x) = (y);\
    15. (y) = tem;\
    16. }
    17. void main()
    18. {
    19. STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
    20. P3 = 0xFF;
    21. UartS1_Init(VBAUD_8BITS,G1,T2,9600);
    22. STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
    23. SundBuzzerx10ms(50);
    24. Delay10xms(50, FSCLK);
    25. SundBuzzerx10ms(50);
    26. while(1)
    27. {
    28. swap(t1,t2);
    29. memset(mstr,0,strlen(mstr));
    30. sprintf(mstr,"t1 = %d\r\n",t1);
    31. UartS1_SendString(mstr);
    32. memset(mstr,0,strlen(mstr));
    33. sprintf(mstr,"t2 = %d\r\n",t2);
    34. UartS1_SendString(mstr);
    35. Delay10xms(200, FSCLK);
    36. }
    37. }
    38. //End of main()
    39. //*********************************************
    40. void SundBuzzerx10ms(ui8 x)
    41. {
    42. BUZZER = 0;
    43. Delay10xms(x, FSCLK);
    44. BUZZER = 1;
    45. }

    编译,下载到单片机,在串口助手的中看到的结果如下:

     从结果可以看出,已经实现了互换。

  • 相关阅读:
    btree,hash,fulltext,Rtree索引类型区别及使用场景
    Ubuntu 安装Kafka
    神经网络训练数据集大小,神经网络输入图片大小
    抽象工厂模式与工厂方法模式代码结构的区别
    华为OD机考:0023-磁盘容量排序
    「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南
    第十四届蓝桥杯模拟题第三期
    Go语言:基础练习--删除数组指定元素
    ensp——防火墙安全策略配置实验
    C++笔试题之n阶楼梯问题:每次只能走1阶或2阶,有多少种方法走完
  • 原文地址:https://blog.csdn.net/billliu66/article/details/126315073