C语言宏函数(宏定义函数)的用法,非常详细

 
在C语言中,使用 #define 命令定义宏时可以带有参数,这点和函数比较类似,所以也被称为宏函数或者宏定义函数;当然,你也可以直接叫它带参数的宏或者带参宏

在宏定义中出现的参数称为“形参”,在宏调用中出现的参数称为“实参”;宏函数在展开过程中不仅要进行文本替换,还要用实参去替换形参。

宏函数的定义格式如下:

#define 宏名(参数列表) (宏体)

其中,宏名是我们给宏函数起的名字,参数列表是宏函数接受的参数,宏体则是宏展开后要替换的内容。需要注意的是,宏体通常要用括号( )包围起来(虽然不是必须的),以避免可能的运算符优先级问题。

宏定义是一种简单粗暴的文本替换,不会为参数分配内存,所以参数只有一个名字,不用为它指明类型。

宏函数的调用格式为:

宏名(实参列表)


可以使用宏来定义简单的函数,这些宏在调用时会被直接展开,而不会像普通函数那样产生调用开销(主要是数据的入栈和出栈)。

【示例1】一个简单的宏定义函数:
#include <stdio.h>

//定义宏函数
#define SQUARE(x) ((x) * (x))

int main() {
    int num = 5;
    int result = SQUARE(num);  // 调用宏函数
    printf("5 的平方是:%d\n", result);
    return 0;
}
运行结果:

5 的平方是:25

在这个例子中,SQUARE(x) 定义了一个计算平方的宏函数。当我们在代码中使用 SQUARE(num) 时,预处理器先用 num 替换宏体中的 x,然后再用处理过的宏体替换 SQUARE(num),最终的替换结果是:

int result = ((num) * (num));

整个过程进行了两层替换。

【示例2】稍微复杂一点的宏定义函数:
#include <stdio.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main() {
    int x = 10, y = 20;
    printf("x 和 y 中的较大值是:%d\n", MAX(x, y));
    return 0;
}
运行结果:

x 和 y 中的较大值是:20

当我们在代码中调用 MAX(x, y) 时,预处理器会将其展开为 ((x) > (y) ? (x) : (y))。

对宏函数的说明

1) 宏函数定义中,形参之间可以出现空格,但是宏名和形参列表之间不能出现空格。例如把:
#define MAX(a,b) ((a>b)?a:b)
写作:
#define MAX (a,b) ((a>b)?a:b)
将被认为是无参宏定义,宏名 MAX 代表字符串(a,b) ((a>b)?a:b)。宏展开时,宏调用语句:
max = MAX(x,y);
将变为:
max = (a,b) ((a>b)?a:b)(x,y);
这显然是错误的。

2) 宏函数不会为形参分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去替换形参,因此实参必须要指明数据类型。

这一点和函数是不同的:在函数中,形参和实参是两个不同的变量,都有自己的作用域,调用时要把实参的值传递给形参;而在宏函数中,只是简单的文本替换,不存在值传递的问题。

例如:输入 n,输出 (n+1)^2 的值。
#include <stdio.h>

#define SQ(y) ((y)*(y))

int main(){
    int a, sq;
    printf("input a number: ");
    scanf("%d", &a);
    sq = SQ(a+1);
    printf("sq=%d\n", sq);
    return 0;
}
运行结果:

input a number: 9↙
sq=100

第 3 行为宏函数,形参为 y。第 9 行宏调用中实参为 a+1,是一个表达式,在宏展开时,用 a+1 代换 y,再用 (y)*(y) 代换 SQ,得到如下语句:

sq = ((a+1)*(a+1));

你看,这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再传递给形参,而宏展开中对实参表达式不作计算,直接按照原样替换。

3) 在宏函数中,宏体内的形参通常要用括号包围起来,以避免出错。例如上例中的宏函数,((y)*(y)) 中的 y 都用括号包围,因此结果是正确的。如果去掉括号,把程序改为以下形式:
#include <stdio.h>

#define SQ(y) (y*y)

int main(){
    int a, sq;
    printf("input a number: ");
    scanf("%d", &a);
    sq = SQ(a+1);
    printf("sq=%d\n", sq);
    return 0;
}
运行结果为:

input a number: 9↙
sq=19

同样输入 9,但结果却是不一样的。问题在哪里呢?这是由于宏展开只是简单的符号替换的过程,没有任何其它的处理。宏替换后将得到以下语句:
sq = (a+1*a+1);
由于 a 为 9,所以 sq 的值为 19。这显然与本意相违,因此参数两边的括号是不能少的。

另外,只在参数两边加括号还是不够的,通常还要给整个宏体加括号。请看下面C语言代码:
#include <stdio.h>

//宏体不加括号
#define SQ(y) (y)*(y)

int main(){
    int a,sq;
    printf("input a number: ");
    scanf("%d", &a);
    sq = 200 / SQ(a+1);
    printf("sq=%d\n", sq);
    return 0;
}
与前面的代码相比,只是把宏调用语句改为:
sq = 200/SQ(a+1);
运行程序后,如果仍然输入 9,那么我们希望的结果为 2,但实际情况并非如此:

input a number: 9↙
sq=200

为什么会得这样的结果呢?分析宏调用语句,在宏展开之后变为:
sq=200/(a+1)*(a+1);
a 为 9 时,由于/*运算符优先级相同,并且都是左结合性,所以先计算 200/(a+1),结果为 20,再计算 20*(a+1),最后得到 200。

由此可见,对于宏函数中的宏体,不仅要在形参两侧加括号,还要在整个宏体两侧也加括号。