介绍宏定义(#define)
1.概念及无参宏
一个最简单的宏的形式如下:
#define 宏名 替换文本
每个#define行(即逻辑行)由三部分组成:
第一部分是指令 #define 自身,“#”表示这是一条预处理命令,“define”为宏命令。
第二部分为宏(macro),一般为缩略语,其名称(宏名)一般大写,而且不能有空格,遵循C变量命令规则。“替换文本”可以是任意常数、表达式、字符串等。
在预处理工作过程中,代码中所有出现的“宏名”,都会被“替换文本”替换。这个替换的过程被称为“宏代换”或“宏展开”(macro expansion)。“宏代换”是由预处理程序自动完成的。在C语言中,“宏”分为两种:无参数和有参数。
1.1 无参宏
无参宏:指宏名之后不带参数,上面最简单的宏就是无参宏。
#define M 5 // 宏定义
#define PI 3.14 //宏定义
int a[M]; // 会被替换为: int a[5];
int b = M; // 会被替换为: int b = 5;
printf("PI = %.2f\n", PI); // 输出结果为: PI = 3.14
注意①:宏不是语句,结尾不需要加“;”,否则会被替换进程序中,如:
#define N 10; // 宏定义
int c[N]; // 会被替换为: int c[10;];
//error:… main.c:133:11: Expected ']'
以上几个宏都是用来代表值,所以被成为类对象宏(object-like macro,还有类函数宏,下面会介绍)。
注意②:如果要写宏不止一行,则在结尾加反斜线符号使得多行能连接上,如:
#define HELLO "hello \
the world"
注意第二行要对齐,否则,如:
#define HELLO "hello the wo\
rld"
printf("HELLO is %s\n", HELLO);
//输出结果为: HELLO is hello the wo rld
也就是行与行之间的空格也会被作为替换文本的一部分
而且由这个例子也可以看出:宏名如果出现在源程序中的“”内,则不会被当做宏来进行宏代换。
注意③:宏可以嵌套,但不参与运算:
#define M 5 // 宏定义
#define MM M * M // 宏的嵌套
printf("MM = %d\n", MM); // MM 被替换为: MM = M * M, 然后又变成 MM = 5 * 5
宏代换的过程在上句已经结束,实际的 5 * 5 相乘过程则在编译阶段完成,而不是在预处理器工作阶段完成,所以宏不进行运算,它只是按照指令进行文字的替换操作。
再强调下,宏进行简单的文本替换,无论替换文本中是常数、表达式或者字符串等,预处理程序都不做任何检查,如果出现错误,只能是被宏代换之后的程序在编译阶段发现。
注意④:宏定义的位置
宏定义必须写在函数之外,其作用域是 #define 开始,到源程序结束。如果要提前结束它的作用域则用 #undef 命令,如:
#define M 5 // 宏定义
printf("M = %d\n", M); // 输出结果为: M = 5
#define M 100 // 取消宏定义
printf("M = %d\n", M); // error:… main.c:138:24: Use of undeclared identifier 'M'
注意⑤:可以用宏定义表示数据类型,可以使代码简便:
#define STU struct Student // 宏定义STU
struct Student{ // 定义结构体Student
char *name;
int sNo;
};
STU stu = {"Jack", 20}; // 被替换为:struct Student stu = {"Jack", 20};
printf("name: %s, sNo: %d\n", stu.name, stu.sNo);
注意⑥: 重复定义宏
如果重复定义宏,则不同的编译器采用不同的重定义策略。有的编译器认为这是错误的,有的则只是提示警告。
注意总结:这些简单的宏主要被用来定义那些显式常量,而且会使得程序更加容易修改,特别是某一常量的值在程序中多次被用到的时候,只需要改动一个宏定义,则程序中所有出现该变量的值都可以被改变。而且宏定义还有更多其他优点,如使得程序更容易理解,可以控制条件编译等。
1.2 #define 与 #typedef 的区别
两者都可以用来表示数据类型,如:
#define INT1 int
typedef int INT2;
两者是等效的,调用也一样:
INT1 a1 = 3;
INT2 a2 = 5;
但当如下使用时,问题就来了:
#define INT1 int *
typedef int * INT2;
INT1 a1, b1;
INT2 a2, b2;
b1 = &m; //... main.c:185:8: Incompatible pointer to integer conversion assigning to 'int' from 'int *'; remove &
b2 = &n; // OK
因为 INT1 a1, b1; 被宏代换后为: int * a1, b1;即定义的是一个指向int型变量的指针 a1 和一个int型的变量b1.而INT2 a2, b2;表示定义的是两个变量a2和b2,这两个变量的类型都是INT2的,也就是int *的,所以两个都是指向int型变量的指针。
所以两者区别在于,宏定义只是简单的字符串代换,在预处理阶段完成。而typede不是简单的字符串代换,而是可以用来做类型说明符的重命名的,类型的别名可以具有类型定义说明的功能,在编译阶段完成的。
1.3 有参宏
C语言中宏是可以有参数的,这样的宏就成了外形与函数相似的类函数宏。
有参宏格式:
# define MEAN(X,Y) (((X)+(Y))/2)
其中MEAN(X,Y) :宏名, (((X)+(Y))/2):替换文本,x、y:宏的参数。
宏调用:宏名(实参表)
printf(“MEAN = %d\n”, MEAN(7, 9)); // 输出结果: MEAN = 8
和函数类似,在宏定义中的参数成为形式参数,在宏调用中的参数成为实际参数。