1、前言

编译一个C程序的第一个步骤被称为预处理,也就是在源代码编译之前对其进行一些文本替换的操作,而一个常见的约定是把宏名字全部大写(用以区分宏和函数,因为在某些场合我们会以宏的形式来实现函数的功能)。

2、宏的常用场合

为了便于一些通用代码模板的维护与扩展,我们在实际开发中会经常使用宏定义。使用#define指令,你可以把任何文本替换到程序中。这里有几个例子:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(1)

第1个定义只是为关键字register创建了一个简短的别名。第2个定义用一个更具描述性的符号来代替一种用于实现无限循环的for语句类型。最后一个#define定义了一种简短记法,以便在switch语句中使用。它自动的把一个break放在每个case之前,原因在于在switch语句中,不遇到break就不会主动退出,一直到语句执行完毕,而很多人常常会在case分支忘记加上break。值得注意的是,如果定义中的语句非常长,它可以分成几行,除了最后一行之外,每行的末尾都要加上一个反斜杠,如下所示:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(2)

3、宏与函数

宏非常频繁的用于执行简单的计算,比如在两个表达式中寻找较大(或较小)的一个:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(3)

为什么不用函数来完成这个任务呢?有两个原因。首先,用于调用和从函数返回的代码很可能比实际执行这个小型计算工作的代码更大,所以使用宏比使用函数在程序的规模和速度方面都更胜一筹。但是,更为重要的是,函数的参数必须声明为一种特定的数据类型,所以它只能在类型合适的表达式上使用。反之,上面这个宏可以用于整型、长整型,浮点型以及其他任何可以用‘>’操作符比较大小的类型。也就是说,宏与类型无关。和函数相比的话,宏也是有缺点的,不利之处就在于每次使用宏时,一份宏定义的代码都将重新拷贝并插入程序中,除非宏非常短,否则使用宏可能会大幅度增加程序的长度。

正如上面所说,宏与函数不同,宏在连续几个调用中所接收的参数类型可以不同,只是简单的对字符串进行替换而已。特别需要注意的一点是:在宏的扩展中,空格会对扩展的结果造成很大的影响,示例如下:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(4)

被扩展为:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(5)

而:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(6)

则被扩展为:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(7)

造成的结果是:他们所表示的意思天差地别(没有看明白的小伙伴欢迎在评论区交流哦)。

另外给大家补充一个小技巧,平常工作的开发中不一定用得到,理解这个思想就行。如下:Steve Bourne在编写UNIX第七版的shell(命令解释器)时,采用了C预处理器(也就是宏#define)使C语言看上去更像是shell编程里的风格,shell编程代码中有显示的“结束语句”提示,诸如if...fi或者case...esac等,调试起来会更容易。Steve认为仅仅一个“}”是不够的,因此他建立了一批宏定义如下:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(8)

这样,就可以像下面这样编写代码:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(9)

再看一下相应的C代码:

宏程序的各种参数及设置(每个代码工程都在使用的宏定义)(10)

本文章结束之前提一个小问题:上图中的C代码是否有错呢?如果有错的话错在哪?没错的话为什么在函数名和函数体之间有奇怪的s1和s2的声明?欢迎在评论区交流。

请关注笔者,后续会定期更新C编程相关知识和技巧,我们一起学习一起进步。

,