C语言宏

C语言的宏可以分为宏定义和宏函数,宏定义又可以叫做对象式宏,在编译时直接把定义的内容替换到源码处,只是一个文本替换功能,不会进行计算。宏函数又叫做类函数宏,跟函数的使用类似,但是他的本质也是文本的替换,这就有产生很多注意事项。

#define N 10 //宏定义
#define max(a,b) (a>b? a:b) //宏函数

不管是宏定义还是宏函数,本质都是文本替换,把宏名称后面的文本参数替换到源码处,因此我们可以分为没有参数的宏和有参数的宏。

没有参数的宏

对于没有参数的宏,处理很简单,直接替换到源码就好了。

#define N 10 
#define six 2+4
#define print printf("打印的值为:");
print       //这里我不加分号,在宏定义里加,目的是让宏定义的文本替换更形象
printf("%d",N*six);

所以上述打印的值是多少呢?编译器把宏名称替换进去后,是这样的

printf("打印的值为:");
printf("%d",10*2+4);

可以看到,打印出来的值是24,而不是60,因为它是单纯文本替换,不计算,所以使用宏时要加上括号

#define N 10 
#define six (2+4)
printf("%d",N*six);

这样才能打印出60。

printf("%d",10*(2+4));

带参数的宏

前面最大值宏函数#define max(a,b) (a>b? a:b)就是带了参数的宏。这里的替换文本也加了括号,是不是随意使用都没有问题呢?不是!

#define max(a,b) (a>b? a:b)
max(3,4==4);

在替换时,ab是形参,34==4(1)是实参,参数之间用逗号隔开宏名称和括号之间不能有空格,否则就成为不带参数的宏,把(a,b) (a>b? a:b)替换进去了。

上面代码我们期望返回的是3,因为b=(4==4)的值是1,3大于1,但其实返回的是1。我们来替换一下,形参替换为实参,宏名替换为其他部分。

 (3>4==4? 3:4==4)

因为>==优先级相同,所以运算从左往右,因此逻辑表达式值为0,返回1即4==4

所以平时使用宏代码时,一定要注意,因为很容易出错。上述代码应该改为:

#define max(a,b) ((a)>(b)? (a):(b))
max(3,4==4);

每个形参都加括号,这样才能到达我们的目的。

对于宏还有几点注意:

  1. 宏定义中反斜杠的作用:对于比较长的替换文本,需要换行时应加入反斜杠,然后续写后面的内容。其中函数内代码换行也是加入反斜杠的,但是由于不用加也可以,为了代码美观,所以函数内单条一般不加反斜杠,直接换行。但是函数外则必须加入反斜杠换行才能进行续写。



2. 函数和宏函数的区别:我们说了宏只是文本替换,不做计算,虽然函数和带参数的宏用法类似,但是函数的操作不一样,它涉及到参数传递(将实参值赋值给形参)、函数调用和函数返回操作(入栈出栈)、返回值的传递,所以宏函数必然是要比函数的效率高的。
3. 宏的作用域:
你无法再次使用 #define 命令重新定义一个已经被定义为宏的标识符,除非重新定义所使用的替换文本与已经被定义的替换文本完全相同。如果该宏具有形参,重新定义的形参名称也必须与已定义形参名称的一样。
如果想改变一个宏的内容,必须首先使用下面的命令取消现在的定义:
#undef 宏名称
执行上面的命令之后,标识符“宏名称”可以再次在新的宏定义中使用。如果上面指定的标识符并非一个已定义的宏名称,那么预处理器会忽略这个 #undef 命令。
标准库中的多个函数名称也被定义成了宏。如果想直接调用这些函数,而不是调用同名称的宏,可以使用#undef 命令取消对这些宏的定义。即使准备取消定义的宏是带有参数的,也不需要在 #undef 命令中指定参数列表。如下例所示:

	#include <ctype.h>
	#undef isdigit          // 移除任何使用该名称的宏定义
	 /* ... */
	if ( isdigit(c) )               // 调用函数isdigit()
	 /* ... */

当某个宏首次遇到它的 #undef 命令时,它的作用域就会结束。如果没有关于该宏的 #undef 命令,那么它的作用域在该翻译单元结束时终止。

其他关于带参数的宏的用法,如可选参数、字符串化运算符(带#号)、 记号粘贴运算符等,参考文献1

参考文献:

  1. C语言宏的定义和宏的使用方法(#define)
  2. C语言基础--宏函数
posted @ 2023-09-17 11:11  Gofan  阅读(129)  评论(0编辑  收藏  举报