C++ 宏定义

一、宏定义

1.1、宏概念

宏定义简单点说就是查找替换(文本替换),C中的宏分为两类,对象宏和函数宏。宏定义的一般形式为:

#define  宏名  字符串

注意:

  • 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换
  • 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束

1.2、宏作用阶段

编译一个C语言程序的第一步骤就是预处理阶段,这一阶段就是宏发挥作用的阶段。C预处理器在源代码编译之前对其进行一些文本性质的操作,主要任务包括删除注释、插入被#include进来的文件内容、定义和替换由#define 定义的符号以及确定代码部分内容是否根据条件编译(#if )来进行编译。”文本性质”的操作,就是指只是简单粗暴的进行文本替换,而不考虑其中任何的语义内容。

1.3、宏函数

配合#、##一起使用函数宏可以节省大量的工作量,例如:获取环境变量的一系列操作,可以通过宏函数生成实际的函数定义,如下:

#define ENV_MAX_LEN (256)
#define DEFINE_FUNC_ENV(ENV, default)                                                                  \
    const char *CFG_Get##ENV()                                                                         \
    {                                                                                                  \
        do {                                                                                           \
            static char ENV##Var[ENV_MAX_LEN] = {'\0'};                                                \
            char *value = getenv(#ENV);                                                                \
            if (IS_NULL(value)) {                                                                      \
                ASSERTEX_NO_RET(IS_NOT_NULL(value), LOG_LEVEL_WARN, "env (%s) not set, use value(%s)", \
                     #ENV, default);                                                                   \
                return default;                                                                        \
            }                                                                                          \
            errno_t retCode = strncpy_s(ENV##Var, ENV_MAX_LEN, value, strlen(value));                  \
            ASSERTEX((retCode == EOK), "", LOG_LEVEL_ERR, "strncpy_s(%s) failed(%d).", value, retCode); \
            return ENV##Var;                                                                           \
        } while (0);                                                                                   \
    }


// 定义函数,从环境变量中获取服务基本配置
DEFINE_FUNC_ENV(APPID, NULL)
DEFINE_FUNC_ENV(APPNAME, "")

// 定义函数,从环境变量中获取服务的位置信息
DEFINE_FUNC_ENV(PODNAME, "defaultPod")
DEFINE_FUNC_ENV(NODE_ID, "defaultNode")
DEFINE_FUNC_ENV(NodeID, "defaultNode")
DEFINE_FUNC_ENV(ContainerID, "defaultContainerID")
DEFINE_FUNC_ENV(HOSTING_SERVER_IP, "127.0.0.1")

1.4、宏函数与普通函数的区别

宏函数与普通函数主要区别如下:

  • 宏函数的本质是进行文本替换,并没有实际函数调用(压栈、退栈)带来的开销
  • 宏函数不会检查参数的类型,普通的函数调用会检查参数类型

二、宏里面特殊字符的作用

2.1、#(stringizing)字符串化操作符

将宏定义中的传入的参数名转换成用一对双引号括起来的参数名字符串,例如:

#define ASSERTEX_NO_RET_EQ(a, b) \
    ASSERTEX_NO_RET((a == b), LOG_LEVEL_ERR, "expect " #a "(%d) equals " #b "(%d) failed", a, b)

注意:

  • 其只能用于有传入参数的宏定义中,且在宏定义体中使用#时,#必须位于宏参数前面
  • 忽略传入参数名前后的空格
#define toStr(str) #str
char *str = toStr(  123  );  // 扩展成 char *str = "123"

2.2、@#(charizing)字符化操作符

将传的单字符参数名转换成字符,以一对单引用括起来,如下:

#define makechar(x) #@x
char ch = makechar(b);  // 扩展成 char ch = 'b'

2.3、##(token-pasting)符号链接操作符

将多个宏参数名拼接成一个参数名,是一种分割链接方式,它的作用是先分隔,然后进行强制连接,如下:

#define exampleNum(n) num##n
int num9 = 9;
int num = exampleNum(9);  // 扩展成 int num = num9;


#define TYPE(type, name) type name##_##type##_type
TYPE(int, a); // 扩展成 int a_int_type;

注意:当用##连接形参时,##前后的空格可有可无。如:#define exampleNum(n) num ## n 相当于 #define exampleNum(n) num##n

2.4、/行继续操作符

当定义的宏不能用一行表达完整时,可以用"/"表示下一行继续此宏的定义

// 判断返回值,并打印信息和返回错误码
#define ASSERTEX(expr, ret, level, fmt, va...) \
    do {                                       \
        if (!(expr)) {                         \
            STD_LOG(level, fmt, ##va);         \
            return ret;                        \
        }                                      \
    } while (0)

三、可变参数宏与__VA_ARGS__ 宏

3.1、VA_ARGS

在GNU C中,从C99开始,宏可以接受可变数目的参数,就象可变参数函数一样。和函数一样,宏也用三个点…来表示可变参数。VA_ARGS 宏用来表示可变参数的内容,简单来说就是将左边宏中 … 的内容原样抄写在右边__VA_ARGS__ 所在的位置。如下:

#include <stdio.h>
#define debug(...) printf(__VA_ARGS__)
int main(void)
{
    int year = 2018debug("this year is %d\n", year);  //效果同printf("this year is %d\n", year);
}

3.2、可变参数名称

通过一些语法,你可以给可变参数起一个名字,而不是使用__VA_ARGS__ ,如下例中的args:

#include <stdio.h>
#define debug(format, args...) printf(format, args)
int main(void)
{
    int year = 2018debug("this year is %d\n", year);  //效果同printf("this year is %d\n", year);
}

3.3、无参传入情况

可变参数宏中的可变参数必须至少有一个参数传入,不然会报错,为了解决这个问题,需要一个特殊的“##”操作,如果可变参数被忽略或为空,“##”操作将使预处理器(preprocessor)去除掉它前面的那个逗号。如下例所示:

#include <stdio.h>
#define debug(format, args...) printf(format, ##args)
int main(void)
{
    int year = 2018debug("hello, world");  //只有format参数,没有args可变参数
}

四、条件宏

下面是常用的条件宏,如下:

宏名功能
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#define定义宏
#endif结束一个#if……#else条件编译块

为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制,可以使用下面的形式

#ifndef _MY_H_
#define _MY_H_
// 要包含的内容,比如一些函数声明,一些结构体定义等。
#endif
  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值