#define 标识符 替换列表
替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使用标识符表示一常量,或称符号常量。#define PI 3.1416 //正确,该行#前允许有空格 int a;#define N 5 //错误,该行#前不允许有空格外的其他字符
#define N =5 //虽语法正确,但预处理器会把N替换成=5 int a[N]; //错误,因为宏替换之后为 int a[=5];宏定义不是语句,是预处理指令,故结尾不加分号。如果不小心添加了分号,虽然有时该宏定义没问题,但在宏替换时,可能导致 C 语法错误,或得不到预期结果。例如:
#define N 5; //虽语法正确,但会把N替换成5; int a[N]; //语法错误,宏替换后,为int a[5;];错误
#define N 3+2 int r=N*N;宏替换后为:
int r=3+2*3+2; //r=11如果采用如下形式的宏定义:
#define N (3+2) int r=N*N;则宏替换后,为:
int r=(3+2)*(3+2); //r=25
#define USA "The United States of America"该宏定义中替换列表为字符串常量,如果该串较长,或为了使替换列表的结构更清晰,可使用续行符 \ 把该串分若干行来写,除最后一行外,每行行尾都必须加续行符 \。
printf("%s\n",USA);则输出结果为:The United States of America
#define 标识符(参数1,参数2,...,参数n) 替换列表
例如,求两个参数中最大值的带参宏定义为:#define MAX(a,b) ((a)>(b)?(a) : (b))当有如下语句时:
int c=MAX(5,3);预处理器会将带参数的宏替换成如下形式:
int c=((5)>(3)?(5) : (3));故计算结果c=5。
#undef 标识符
说明:#define MAX (a,b) ( (a) > (b) ? (a) : (b) ) //错误的带参宏定义格式2) 宏替换列表中每个参数及整个替换列表,都必须用一对小括号 () 括起来,否则可能会出现歧义。
#include <stdio.h> #define MUL(a,b) (a*b) int main (void) { int c; c=MUL(3,5+1); printf("c=%d\n",c); return 0; }分析:
#include <stdio.h> #define MUL(a,b) ((a)*(b))//修改处1 int main (void) { int c; c=MUL(3,(5+1);//修改处2 printf("c=%d\n",c); return 0; }
接下来将从调用发生时间、参数类型检查、参数是否需要空间、运行速度等几个主要方面进行对比分析带参宏定义与函数调用的差异。
在源程序进行编译之前,即预处理阶段进行宏替换;而函数调用则发生在程序运行期间。
函数参数类型检查严格。程序在编译阶段,需要检查实参与形参个数是否相等及类型是否匹配或兼容,若参数个数不相同或类型不兼容,则会编译不通过。
在预处理阶段,对带参宏调用中的参数不做检查。即宏定义时不需要指定参数类型,既可以认为这是宏的优点,即适用于多种数据类型,又可以认为这是宏的一个缺点,即类型不安全。故在宏调用时,需要程序设计者自行确保宏调用参数的类型正确。
函数调用时,需要为形参分配空间,并把实参的值复制一份赋给形参分配的空间中。而宏替换,仅是简单的文本替换,且替换完就把宏名对应标识符删除掉,即不需要分配空间。
函数在编译阶段需要检查参数个数是否相同、类型等是否匹配等多个语法,而宏替换仅 是简单文本替换,不做任何语法或逻辑检查。
函数在运行阶段参数需入栈和出栈操作,速度相对较慢。
由于宏替换是文本替换,即如果需替换的文本较长,则替换后会影响代码长度;而函数不会影响代码长度。
故使用较频繁且代码量较小的功能,一般采用宏定义的形式,比采用函数形式更合适。前面章节频繁使用的 getchar(),准确地说,是宏而非函数。
为了使该宏调用像函数调用,故把该宏设计成了带参数的宏定义:
#define getchar() getc(stdin)
故调用该宏时,需要加括号,即传空参数:getchar()。
Copyright © 广州京杭网络科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有