宏(宏观)本质上就是代码片段,通过别名来使用。在编译前的预处理中,宏会被替换为真实所指代的代码片段,即下图中预处理器处理的部分。
C/c++代码编译过程——图片来自ntu.edu.sg
根据用法的不同,分两种,对象和函数。前者用于对象对象,后者用于函数方法。
C/c++代码编译过程中,可通过相应参数来获取到各编译步骤中的产出,比如想看被预处理编译之后的宏,使用gcc使加上- e参数。
美元gcc - e macro.c
通过#定义指令定义一个宏。
#定义NAME_OF_MACRO值
比如,以下代码定义了一个名为BUFFER_SIZE的宏,指1024年代这个数字。
#定义BUFFER_SIZE 1024
使用时,
foo=(char *) malloc (BUFFER_SIZE);
使用预处理器编译:
美元gcc - e test.c
编译结果:
foo=(char *) malloc(1024), >之前<强>多行强>
宏的定义是跟随#定义在一同一行内的,但可通过反斜杠\实现换行从而定义出多行的宏。
多行的宏经过编译后会还原到一行中。
test.c
# include & lt; stdio.h> #定义GREETING_STR \ “你好\ 世界” int main () {printf (GREETING_STR);}编译后:
int main () { printf (“hello world”); 返回0; }<>强宏展开时的顺序
强>宏的展开是在处理源码时按照其出现位置进行的,如果宏定义有嵌套关系,也是层层进行展开,比如:
# include & lt; stdio.h> #定义GREETING_NAME“wayou” GREETING_NAME # define问候“你好。 int main () { printf(问候); 返回0; }首先遇到问候,将其展开成GREETING_NAME“wayou”,然后发现另一个宏GREETING_NAME,将其展开最后得到“你好,”“wayou”,所以编译后的代码为:
int main () { printf(“你好”、“wayou”); 返回0; }其展开的顺序并不是宏定义时的顺序,为了验证,可将上面示例代码中两个宏的定义调换一下,得到:
——# define GREETING_NAME“wayou” GREETING_NAME # define问候“你好。 + # define GREETING_NAME“wayou”再次编译查看产出,会发现没有区别,也不会报问候中所依赖的GREETING_NAME找不到的错。其实# define只是告诉编译器定义了这么个宏,而具体的求值,则是使用宏的地方才开始的。
像下面这样,当宏存在覆盖时,会以新的为准,其结果为37。
#定义BUFSIZE 1020 #定义TABLESIZE BUFSIZE # undef BUFSIZE #定义BUFSIZE 37
对象类型的宏看起来就像普通的数据对象,故名。多用于数字常量的情形下。且宏名一般使用全大写形式方便识别。像上面示例中,都是对象的。
也可定义出使用时像是方法调用一样的宏,这便是功能类型的宏。
#定义lang_init () c_init () lang_init ()//编译后 c_init ()函数类型的宏只在以方法调用形式使用时才会被展开,即名称后加括号,否则会被忽略。当宏名和函数名重名时,这一策略就会显得有用了,比如:
走读生空白foo(无效); #定义foo()/* */内联优化版本 … foo (); funcptr=foo;这里foo()的调用会来自宏里面定义的那个函数,而funcptr会正确地指向函数地址,如果后者也被宏展开,则成了funptr=foo()显然就不对了。
函数类型的宏在定义时需注意,宏名与后面括号不能有空格,否则就是普通的对象类型对象。
C/c++中宏/宏的深入讲解