宏(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;
}

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

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

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐