宏(Macro)本质上就是代码片段,通过别名来使用。可简单地将宏编程理解为:按一定规则将文本定义为一个标志符,在编程时直接引用标志符替代这些文本进行编程,在程序编译时,再将标志符替换回预定义的文本的过程。C语言预编译器主要实现宏替换功能。Keil C51与标准C语言大同小异,也有宏系统。
宏定义格式如下:
#define 标志符[(参数表)] 字符串
注意字符串结尾处不可有分号。宏定义标准符后面可以带小括号,括号里面也可以带参数表,也可以不带参数。带小括号的宏定义类似于函数定义,我们可以把这种宏叫做宏函数。
宏的主要应用如下:
1. 定义别名,如下面代码:
- #ifndef BYTE
- #define BYTE unsigned char
- #endif
- #ifndef byte
- #define byte unsigned char
- #endif
- #ifndef ui8
- #define ui8 unsigned char
- #endif
- #ifndef UI8
- #define UI8 unsigned char
- #endif
- #ifndef U8
- #define U8 unsigned char
- #endif
- #ifndef u8
- #define u8 unsigned char
- #endif
- #ifndef uchar
- #define uchar unsigned char
- #endif
-
- #ifndef INT8
- #define INT8 signed char
- #endif
- #ifndef int8
- #define int8 signed char
- #endif
- #ifndef I8
- #define I8 signed char
- #endif
- #ifndef i8
- #define i8 signed char
- #endif
2. 定义常数,如下面代码:
#define FSCLK 30000000L
3. 避免头文件重复包含的宏定义,如下面代码:
- /*config.h
-
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 06/22/2022
- */
- #ifndef __CONFIG_H__
- #define __CONFIG_H__
-
- #define FOSC 30000000UL
-
- //********************************************************
- void SysInit(); //init System speed fastest
-
-
- #endif
4. 条件编译的宏定义,如下面代码:
- /*******************************************************************************
- * 函 数 名 : LcdInit()
- * 函数功能 : 初始化LCD屏
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- #define LCD1602_4PINS
- #ifndef LCD1602_4PINS
- void Lcd1602_Init() //LCD初始化子程序
- {
- LCDWriteCom(0x38); //开显示
- LCDWriteCom(0x0c); //开显示不显示光标
- LCDWriteCom(0x06); //写一个指针加1
- LCDWriteCom(0x01); //清屏
- LCDWriteCom(0x80); //设置数据指针起点
- }
- #else
- void LCDInit() //LCD初始化子程序
- {
- LCDWriteCom(0x32); //将8位总线转为4位总线
- LCDWriteCom(0x28); //在四位线下的初始化
- LCDWriteCom(0x0c); //开显示不显示光标
- LCDWriteCom(0x06); //写一个指针加1
- LCDWriteCom(0x01); //清屏
- LCDWriteCom(0x80); //设置数据指针起点
- }
- #endif
- //*************************************************************************
5. 取代函数定义,宏函数定义与函数定义有些相像,但是其编译执行过程是完全不一样的,宏函数在编译工程中是完全的代码替换,而函数则是编译成机器码仿真特定的地址空间内,调用时需要来回跳转,效率不如宏函数高,但是使用宏函数定义编译后的程序可能会占用更大的程序存储空间。
宏函数定义及应用
1. 无参数表宏函数定义 宏函数名实际就是后面宏函数体的别名,因此定义宏函数在宏函数名前不能有返回值类型,宏函数体实际上是以宏函数名的连续文本,在换行前必须加反斜杠(\),如下面代码示:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define InitP0_PPOut() {\
- P0M1 = 0x00;\
- P0M0 = 0xFF;\
- }
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- InitP0_PPOut();
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
-
编译以上代码 ,结果如下:
下面修改一下代码,多次引用宏函数,修改后的代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define InitP0_PPOut() {\
- P0M1 = 0x00;\
- P0M0 = 0xFF;\
- }
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
再编译,看下编译结果怎么样,结果如下:
可以看出code由2533变成了2553, 现在把宏函数改为函数,并调用一次,改好的代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- void InitP0_PPOut() {
- P0M1 = 0x00;
- P0M0 = 0xFF;
- }
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- InitP0_PPOut();
- //InitP0_PPOut();
- //InitP0_PPOut();
- //InitP0_PPOut();
- //InitP0_PPOut();
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译结果如下:
现在去掉多次调用前的注释,多次调用函数,修改后的代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- void InitP0_PPOut() {
- P0M1 = 0x00;
- P0M0 = 0xFF;
- }
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
通过以上测试可以看出,如果调用一次,使用宏函数比使用函数编译后的代码所占空间更小,如果需要多次调用,函数调用编译后的代码更小。使用宏函数运行速度会更快,因为它没有了,函数间的跳转,这一点前面已经讲过。这样我们就明白了在何时使用宏函数,何时使用函数。
2. 带参数表的宏函数定义 由于宏函数在编译时先由预处理器进行文本替换然后再编译,因此参数列表中的参数不需要,也不能有类型约束,另外规则规定参数展开需要加小括号,否则会得到不可预测的结果。下面是一个正确的宏函数定义:
-
- #define Sum(x, y) {\
- return (x) + (y);\
- }
现将上面的宏函数放入程序中,看能否编译通过,程序代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y) {\
- return (x) + (y);\
- }
-
- int main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
- InitP0_PPOut();
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- /*
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- */
- Sum(8, 7);
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译结果如下:
编译通过。下面我们来试一下能否像函数那样调用,修改程序代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y) {\
- return (x) + (y);\
- }
-
- int a;
-
- int main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- /*
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- */
- a = Sum(8, 7);
- return a;
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
编译结果如下:
编译不能通过。现在再修改一下宏函数定义及程序代码,修改后的程序如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y,z) {\
- z = (x) + (y);\
- }
-
- int a;
-
- int main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- /*
- while(1)
- {
- UartS1_SendString("Uart S1 Test!");
- Delay10xms(200, FSCLK);
- }
- */
- Sum(8, 7, a);
- return a;
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译结果如下:
编译通过。上面宏函数得到的结果是否是正确的呢?下面来做下测试,先修改程序代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y,z) {\
- z = (x) + (y);\
- }
-
- ui8 mstr[20] = {0};
- int a = 0;
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
- Sum(8, 7, a);
-
- while(1)
- {
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"a = %d\n",a);
- UartS1_SendString(mstr);
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译结果如下:
编译通过,现在把它下载到单片机,看下结果如何
上面是在串口助手中看到的结果,可以看出 a=15,结果是正确的。注意宏函数中z在展开时没有加括号。先修改一下程序代码,修改后如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y,z) {\
- z = (x) + (y);\
- }
-
- ui8 mstr[20] = {0};
- int a = 0;
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
-
-
- while(1)
- {
- Sum(10, 15, a);
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"a = %d\n",a);
- UartS1_SendString(mstr);
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译下载到单片机,结果如下:
如果将程序修改如下,又会如何呢?修改后的程序代码如下:
结果也是对的。尽管如此,在编程时还是建议在展开时将参数加上小括号,现将宏函数代码修改如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y,z) {\
- (x) = (x) + 1;
- (z) = (x) + (y);\
- }
-
- ui8 mstr[20] = {0};
- int a = 0;
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
-
-
- while(1)
- {
- Sum(10, 1, a);
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"a = %d\n",a);
- UartS1_SendString(mstr);
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译结果如下:
不能通过编译。修改代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(x, y,z) {\
- (z) = (z) + 1;\
- (z) = (x) + (y);\
- }
-
- ui8 mstr[20] = {0};
- int a = 0;
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
-
-
- while(1)
- {
- Sum(10, 1, a);
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"a = %d\n",a);
- UartS1_SendString(mstr);
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译结果如下:
再修改代码,修改后的代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
- #define Sum(z, y,x) {\
- (x) = (x) + 1;\
- (x) = (z) + (y);\
- }
-
- ui8 mstr[20] = {0};
- int a = 0;
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
-
-
- while(1)
- {
- Sum(10, 1, a);
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"a = %d\n",a);
- UartS1_SendString(mstr);
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
再编译,看能否通过,编译结果如下:
编译通过,能看出门道不?如果列表中有更多参数,有会怎样?参与的定义是否与后面的引用相关联?如果你对宏有正确认识,心里一定会有正确答案 。我们知道下面函数不能实现数据交换,该函数代码如下:
- void swap(int x, int y)
- {
- int tem = x;
- x = y;
- y = tem;
- }
如果是宏函数呢?下面我们来试一下。程序代码如下:
- /*main.c
- Designed by Bill Liu
- Version 0.0
- Modified last by Bill Liu on 12/08/2021
- */
-
-
- #include "main.h"
- #include "stcint.h"
-
-
-
- ui8 mstr[20] = {0};
- int t1 = 10;
- int t2 = 15;
- int tem = 0;
-
- #define swap(x,y) {\
- tem =(x);\
- (x) = (y);\
- (y) = tem;\
- }
-
- void main()
- {
-
- STCIO_InitPortsBits(P0|P1|P2|P3|P4, 0xFF, BI_IO);
- P3 = 0xFF;
- UartS1_Init(VBAUD_8BITS,G1,T2,9600);
- STCIO_InitP3Bit(SCT_BIT1, PP_OUT);
-
- SundBuzzerx10ms(50);
- Delay10xms(50, FSCLK);
- SundBuzzerx10ms(50);
-
-
-
- while(1)
- {
- swap(t1,t2);
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"t1 = %d\r\n",t1);
- UartS1_SendString(mstr);
- memset(mstr,0,strlen(mstr));
- sprintf(mstr,"t2 = %d\r\n",t2);
- UartS1_SendString(mstr);
- Delay10xms(200, FSCLK);
- }
- }
- //End of main()
-
- //*********************************************
- void SundBuzzerx10ms(ui8 x)
- {
- BUZZER = 0;
- Delay10xms(x, FSCLK);
- BUZZER = 1;
- }
编译,下载到单片机,在串口助手的中看到的结果如下:
从结果可以看出,已经实现了互换。