宏
宏定义( 宏定义又称为宏代换、宏替换)
在 C 语言中,可以采用命令 #define 来定义宏。该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉。
惯例将宏名称每个字母采用大写,这有助于区分宏与一般的变量
宏不可以递归地展开:如果预处理器在 A 宏的替换文本中又遇到了 A 宏的名称,或者从嵌套在 A 宏内的 B 宏内又遇到了 A 宏的名称,那么 A 宏的名称就会无法展开。
没有参数的宏
没有参数的宏定义,采用如下形式:
-
#define 宏名称 替换文本
“替换文本”前面和后面的空格符不属于替换文本中的内容。替代文本本身也可以为空。下面是一些示例:
-
#define TITLE "*** Examples of Macros Without Parameters ***"
-
#define BUFFER_SIZE (4 * 512)
-
#define RANDOM (-1.0 + 2.0*(double)rand() / RAND_MAX)
标准函数 rand()返回一个伪随机整数,范围在 [0,RAND_MAX] 之间。rand()的原型和 RAND_MAX 宏都定义在标准库头文件 stdlib.h 中。
、
带参数的宏
你可以定义具有形式参数(简称“形参”)的宏。当预处理器展开这类宏时,它先使用调用宏时指定的实际参数(简称“实参”)取代替换文本中对应的形参。带有形参的宏通常也称为类函数宏(function-like macro)。
可以使用下面两种方式定义带有参数的宏:
-
#define 宏名称( [形参列表] ) 替换文本
-
#define 宏名称( [形参列表 ,] ... ) 替换文本
“形参列表”是用逗号隔开的多个标识符,它们都作为宏的形参。当使用这类宏时,实参列表中的实参数量必须与宏定义中的形参数量一样多
当定义一个宏时,必须确保宏名称与左括号之间没有空白符。如果在名称后面有任何空白,那么命令就会把宏作为没有参数的宏,且从左括号开始采用替换文本。
宏只是进行机械地替换
area=3+1*3+4=10
若想有(3+1)*(3+D)
宏的作用域和重新定义
你无法再次使用 #define 命令重新定义一个已经被定义为宏的标识符,除非重新定义所使用的替换文本与已经被定义的替换文本完全相同。如果该宏具有形参,重新定义的形参名称也必须与已定义形参名称的一样。
如果想改变一个宏的内容,必须首先使用下面的命令取消现在的定义:
-
#undef 宏名称
执行上面的命令之后,标识符“宏名称”可以再次在新的宏定义中使用。如果上面指定的标识符并非一个已定义的宏名称,那么预处理器会忽略这个 #undef 命令。
标准库中的多个函数名称也被定义成了宏。如果想直接调用这些函数,而不是调用同名称的宏,可以使用 #undef 命令取消对这些宏的定义。即使准备取消定义的宏是带有参数的,也不需要在 #undef 命令中指定参数列表。如下例所示:
-
#include <ctype.h>
-
#undef isdigit // 移除任何使用该名称的宏定义
-
/* ... */
-
if ( isdigit(c) ) // 调用函数isdigit()
-
/* ... */
当某个宏首次遇到它的 #undef 命令时,它的作用域就会结束。如果没有关于该宏的 #undef 命令,那么它的作用域在该翻译单元结束时终止。
宏展开
在预处理中,预处理器会分析源文件,把它们转换为预处理器记号和空白符。如果遇到的记号是宏名称,预处理器就会展开(expand)该宏;也就是说,会用定义的文本来取代宏名称。出现在字符串字面量中的宏名称不会被展开,因为整个字符串字面量算作一个预处理器记号。
宏定义和函数调用的区别
1.宏替换不占运行时间,只占编译时间;而函数调用则占运行时间(分配单元、保留现场、值传递、返回),所以每次执行都要载入所以执行起来比较慢一些。
2. 定义宏的时候不要在宏及其参数之间键入空格,因为宏替换的时候会把你不经意打的空格当作宏的一部分进去。