Keil C51宏及宏函数的应用

宏(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
  1. 定义常数,如下面代码:
#define FSCLK 30000000L
  1. 避免头文件重复包含的宏定义,如下面代码:
/*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
  1. 条件编译的宏定义,如下面代码:
/*******************************************************************************
* 函 数 名       : 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
 //*************************************************************************
  1. 取代函数定义,宏函数定义与函数定义有些相像,但是其编译执行过程是完全不一样的,宏函数在编译工程中是完全的代码替换,而函数则是编译成机器码仿真特定的地址空间内,调用时需要来回跳转,效率不如宏函数高,但是使用宏函数定义编译后的程序可能会占用更大的程序存储空间。

宏函数定义及应用

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

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

Keil C51宏及宏函数的应用

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

/*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;
}

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

Keil C51宏及宏函数的应用

可以看出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;
}

编译结果如下:

Keil C51宏及宏函数的应用

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

/*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;
}

Keil C51宏及宏函数的应用

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

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

编译结果如下:

Keil C51宏及宏函数的应用

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

/*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;

编译结果如下:

Keil C51宏及宏函数的应用

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

/*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;
}

编译结果如下:

Keil C51宏及宏函数的应用

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

/*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;
}

编译结果如下:

Keil C51宏及宏函数的应用

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

Keil C51宏及宏函数的应用

上面是在串口助手中看到的结果,可以看出 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;
}

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

Keil C51宏及宏函数的应用

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

Keil C51宏及宏函数的应用

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

/*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;
}

编译结果如下:

Keil C51宏及宏函数的应用

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

/*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;
}

编译结果如下:

Keil C51宏及宏函数的应用

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

/*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;
}

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

Keil C51宏及宏函数的应用

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

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

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

Keil C51宏及宏函数的应用

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

Original: https://blog.csdn.net/billliu66/article/details/126315073
Author: Bill66
Title: Keil C51宏及宏函数的应用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/655376/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球