Menu Close

什么是C语言宏 ?什么是C语言带参数宏定义?

宏(Macro)是预处理命令的一种,它允许用一个标识符来表示一个字符串。先看一个例子:

例1.宏的应用

#include <stdio.h>
#define M (n*n+3*n)
int main()
{
    int sum, n;
    printf("Input a number: ");
    scanf("%d", &n);
    sum = 3*M+4*M+5*M;
    printf("sum=%d\n", sum);
    return 0;
}

 

该示例中的语句int sum = 20 + N;,N被100代替了。

#define N 100就是宏定义,N为宏名,100是宏的内容。

在预处理阶段,对程序中所有出现的“宏名”,预处理器都会用宏定义中的字符串去代换,这称为“宏替换”或“宏展开”。

宏定义是由源程序中的宏定义命令#define完成的,宏替换是由预处理程序完成的。

宏定义的一般形式为:

#define 宏名 字符串

#表示这是一条预处理命令,所有的预处理命令都以 # 开头。define是预处理命令。宏名是标识符的一种,命名规则和标识符相同。字符串可以是数字、表达式、if 语句、函数等。

这里所说的字符串是一般意义上的字符序列,不要和C语言中的字符串等同,它不需要双引号。

程序中反复使用的表达式就可以使用宏定义,例如:

#define M (n*n+3*n)

它的作用是指定标识符M来表示(y*y+3*y)这个表达式。在编写代码时,所有出现 (y*y+3*y) 的地方都可以用 M 来表示,而对源程序编译时,将先由预处理程序进行宏代替,即用 (y*y+3*y) 去替换所有的宏名 M,然后再进行编译。

例2.宏的应用2

 #include <stdio.h>
#define M (n*n+3*n)
int main()
{
    int sum, n;
    printf("Input a number: ");
    scanf("%d", &n);
    sum = 3*M+4*M+5*M;
    printf("sum=%d\n", sum);
    return 0;
}

程序的开头首先定义了一个宏 M,它表示 (n*n+3*n) 这个表达式。在 第8行代码中使用了宏 M,预处理程序将它展开为下面的语句:

下面的语句:

sum=3*(n*n+3*n)+4*(n*n+3*n)+5*(n*n+3*n);

需要注意的是,在宏定义中表达式(n*n+3*n)两边的括号不能少,否则在宏展开以后可能会产生歧义。下面是一个反面的例子:

#difine M n*n+3*n

在宏展开后将得到下述语句:

s=3*n*n+3*n+4*n*n+3*n+5*n*n+3*n;

这相当于:

3n2+3n+4n2+3n+5n2+3n

这显然是不正确的。所以进行宏定义时要注意,应该保证在宏替换之后不发生歧义。

对宏定义的几点说明

1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单粗暴的替换。字符串中可以含任何字符,它可以是常数、表达式、if 语句、函数等,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现。

2) 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换。

3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。例如:

#define PI 3.14159

int main()
{
// Code
return 0;
}
#undef PI
void func()
{
// Code
}

表示 PI 只在 main() 函数中有效,在 func() 中无效。

4) 代码中的宏名如果被引号包围,那么预处理程序不对其作宏代替,例如:

例3.宏名被引号包围

#include <stdio.h>
#define OK 100
int main()
{
   printf("OK\n");
   return 0;
}

该例中定义宏名 OK 表示 100,但在 printf 语句中 OK 被引号括起来,因此不作宏替换,而作为字符串处理。

5) 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。

例如:

#define PI 3.1415926
#define S PI*y*y /* PI是已定义的宏名*/

对语句:

printf("%f", S);

在宏代换后变为:

printf("%f", 3.1415926*y*y);

6) 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

7) 可用宏定义表示数据类型,使书写方便。例如:

#define UINT unsigned int

在程序中可用 UINT 作变量说明:

UINT a, b;

应注意用宏定义表示数据类型和用 typedef 定义数据说明符的区别

宏定义只是简单的字符串替换,由预处理器来处理;

而 typedef 是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一个新的名字,将它作为一种新的数据类型。

请看下面的例子:

#define PIN1 int *
typedef int *PIN2; //也可以写作typedef int (*PIN2);

从形式上看这两者相似, 但在实际使用中却不相同。

下面用 PIN1,PIN2 说明变量时就可以看出它们的区别:

PIN1 a, b;

在宏代换后变成:

int * a, b;

表示 a 是指向整型的指针变量,而 b 是整型变量。然而:

PIN2 a,b;

表示 a、b 都是指向整型的指针变量。因为 PIN2 是一个新的、完整的数据类型。

由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟只是简单的字符串替换。在使用时要格外小心,以避出错。

C语言带参数宏定义

C语言允许宏带有参数。在宏定义中的参数称为“形式参数”,在宏调用中的参数称为“实际参数”,这点和函数有些类似。

对带参数的宏,在展开过程中不仅要进行字符串替换,还要用实参去替换形参。

带参宏定义的一般形式为:

#define 宏名(形参列表) 字符串

在字符串中可以含有各个形参。

带参宏调用的一般形式为:

宏名(实参列表);

例如:

#define M(y) y*y+3*y //宏定义
// Code
k=M(5); //宏调用

在宏展开时,用实参 5 去代替形参 y,经预处理程序展开后的语句为k=5*5+3*5

例4.输出两个数中较大的数

#include <stdio.h>
#define MAX(a,b) (a>b) ? a : b
int main()
{
    int x , y, max;
    printf("input two numbers: ");
    scanf("%d %d", &x, &y);
    max = MAX(x, y);
    printf("max=%d\n", max);
    return 0;
}

运行结果:

input two numbers: 10 20
max=20

程序第 2 行定义了一个带参数的宏,用宏名MAX表示条件表达式(a>b) ? a : b,形参 a、b 均出现在条件表达式中。程序第 8 行max = MAX(x, y)为宏调用,实参 x、y 将用来代替形参 a、b。

宏展开后该语句为:

max=(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) 在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去替换形参,因此实参必须要指明数据类型。

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

例5.输入 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

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

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

3) 在宏定义中,字符串内的形参通常要用括号括起来以避免出错。例如上面的宏定义中 (y)*(y) 表达式的 y 都用括号括起来,因此结果是正确的。如果去掉括号,把程序改为以下形式:

例6. 宏的歧义应用

#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;

即使在参数两边加括号还是不够的,请看下面程序:

例7.宏的歧义应用

#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/(9+1),结果为 20,再计算 20*(9+1),最后得到 200。

为了得到正确答案,应该在宏定义中的整个字符串外加括号:

例8.宏的正确定义

#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;
}

由此可见,对于带参宏定义不仅要在参数两侧加括号,还应该在整个字符串外加括号。

例9. Y两边如果不用括号

#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;
}
input number: 9
sq结果: 10

为什么会得这样的结果呢?分析宏调用语句,在宏展开之后变为:

sq=200/(a+1*a+1) = 10 ( 取整的结果)

例10.Y两边不用括号的浮点数结果

#include <stdio.h>
#define SQ(y) (y*y)
int main()
{
    float a,sq;
    printf("input a number: ");
    scanf("%f", &a);
    sq = 200 / SQ(a+1);
    printf("sq=%f\n", sq);
    return 0;
}

200/(9+1*9+1) = 10.526316

sq是实数的结果。

 

 

 

除教程外,本网站大部分文章来自互联网,如果有内容冒犯到你,请联系我们删除!

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Leave the field below empty!

Posted in C语言教程

Related Posts