1 简介
宏在C语言中是一段有名称的代码片段。无论何时使用到这个宏的时候,宏的内容都会被这段代码替换掉。
在C语言中使用#define来定义宏,你可以将任意的有效的标识符定义为宏,但是defined关键字不可以作为宏的名称。在C++中以下的关键字也不可以作为宏的名称:and,and_eq,bitand,bitor,compl,not,not_eq,or,or_eq,xor,xor_eq。
2 两种宏的类型
主要有两种宏,一种是在使用时类似于数据对象称为Object-like宏,另一种在使用时类似于函数调用称为Function-like宏。
2.1 Object-like宏
Object-like宏,可以比较简单的进行代码段的替换。这种方式最常用做表示常量数字。例如:
使用该宏的时候就可以用来替换数字。
预处理器将会把该宏替换为对应的数字,如下所示。
按照惯例,宏一般都写作大写字母。
多行的宏
宏结束于#define的行尾,如果有必要的话,可以在末尾添加反斜杠来将宏定义成多行。
多次宏替换
如果宏定义的代码段依然是宏的话,预处理器会继续进行宏替换的操作。
2.2 Function-like宏
宏还可以被定义成下面的形式,使用该宏的时候,类似于调用函数,这类宏的定义中,宏的名称后面紧跟一堆括 (与括 之间不能有空格)。
调用该类宏的时候,也必须跟一个括 ,如果不跟括 的话,会显示语法错误。
宏的参数
Function-like宏可以接受参数,类似于真正的函数一样。参数必须是有效的C语言标识符,使用逗 隔开
字符串化
字符串化指的是,可以在宏的参数前面加入#,使入参变成字符串。例如:
连接符
在宏中,可以使用两个#将两个符 连接成一个符 。
在该例子中,调用宏A(1)时,NAME为1。A##NAME这个符 连接,即将A和1连接成了一个符 A1,然后执行宏A1的内容,最终打印出来了print A1。
可变参数
定义宏可以接受可变数量的参数,类似于定义函数一样。如下就是一个例子
这种形式的宏,会把…代表的参数扩展到后面的__VA_ARGS__中。在该例子中,就会扩展为fprintf(stderr, “1234rn”)。
如果你的参数比较复杂,上面的myprintf还可以定义为如下的形式,用自定义的名称args来表示参数的含义:
3 预定义宏
3.1 标准预定义宏
标准的预定义宏都是用双下划线开头和结尾,例如__FILE__和__LINE__,表示文件的名称和该行代码的行 。
3.2 常见的GNU C编译器扩展的预定义宏
3.3 系统特定的预定义宏
系统特定的预定义宏,在不同的操作系统和CPU上面,呈现的结果可能会有所不同。例如我的环境是Linux X86_64平台。执行下面的代码
4 C++的命名操作符
前面说过C++ 中有and,and_eq,bitand,bitor,compl,not,not_eq,or,or_eq,xor,xor_eq这些命名不可以用作宏的名称。那么为什么呢?是因为在C++ 中系统将这些关键字预定义成了操作符。
所以在C++ 中,你可以使用命名操作符来代替这些符 。例如:
5 宏的取消
使用#undef可以将已经定义的宏取消掉
如果在#undef之后再使用BUFSIZE就会 错,没有定义BUFSIZE
6 宏的几种常见使用场景
6.1 替代魔法数字
这个是C语言中非常常见的一种用法,增加代码可读性。
6.2 LOG日志
这里定义了LOG宏,可以打印日志,输出当前的代码文件和行数,以及时间和用户定义的内容。自行扩展可以增加更丰富的内容。
注意:这里使用了一个do{} while(0)来包含宏的内容。看似这个do() while(0)没有什么意义,但是这是一个编写宏内多行代码段的好习惯。使用do{}while(0)包含的话,可以作为一个独立的block,进行变量定义等一些复杂的操作,该用法主要是防止在使用宏的过程中出现错误。例如
在这种情况下,if后面没有跟大括 ,我们foo宏里面定义的是两个语句,其中fun2是在if条件判断之外的。这样就不符合我们的预期了。
如果使用大括 来避免上面的错误,还会出现下面的错误:
这里在add(x, y)之后有个分 。会造成else匹配不到if编译错误。所以为了防止发生这些错误,可以使用do{}while(0)将函数体包含。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!