关于#define预处理的讨论
以前看过一本C++的书说常量的定义最好不要用#define而用const,以前总不太明白为什么要新增一种常量类型const来定义。C语言的常规方法#define不是很好吗?
然而,事实上,#define处理的不仅仅是常量,而且包括语言结构……
如果用
#define i 5
定义了一个命名为i的常量,那么如果在函数中
int functionX(int a, int b) { int i; .... }
的话,就会产生错误,C语言预处理器会把所有i都替换为常量5!也就是说,上面的代码实际上是
int functionX(int a, int b) { int 5; .... }
显然会产生错误……
因此,如果真的决定用#define来定义常量的话,对常量的命名必须很小心,否则就会出现一些不希望的错误了。
但是,如果在全局中写
const int i = 5;
定义一个称为i的常量的话,
int functionX(int a, int b) { int i; .... }
就不会出现任何错误,编译器直接把全局的常变量i忽略……(注意常变量的提法,因为用const事实上也是定义一个变量,知识这个变量初始化后不可改变而已。这样做的意义其中之一是可以防止变量被修改。因此称其为常变量。)
当然了,#define的强大功能不仅仅局限于定义常量,它定义的是一个语言结构。
例如
#define add(x,y) (x)+(y)
还可以定义一个类似函数的结构,就像
type add(type x, type y) { return x+y; }
这个type是什么东西?嗯,这就是用#define定义的这个类似函数的结构的强大之处。
如果用#define像上面那样定义一个叫add(x,y)的结构的话,在代码中调用add(a, b)将返回a+b的值,而且a、b没有类型限制,但必须支持“+”操作。编译器的预处理器会把add(a,b)直接转换为a+b出现在调用的位置上。因此,从某种程度上说,这个“函数”就变成了通用函数,即对任何支持“+”操作的数都可以使用,例如double、float、int、long等等类型。
如果只是简单地定义一个int add(int x, int y)的话,add(a, b)就只能支持int类型的“+”操作了。
还有一个关于结构体的应用。
例如,在实际应用中可能会出现如下定义
struct Student { char name[20]; int id; char major[20]; }; struct Teacher { char name[20]; int id; char job[20]; };
事实上,Teacher和Student结构有区别,但也有共同的地方(都有name、id属性)。
如果想用一个函数来输出人的名字和ID,那么必须定义两个函数
void show_student(struct Student s) { printf("%d %s", s.id, s.name); } void show_teacher(struct Teacher t) { printf("%d %s", t.id, t.name); }
这样就可能有点浪费(写了两个几乎一样的函数)。然而
#define show(p) show_attr1(p.id, p.name) void show_attr1(int id, char *name) { printf("%d %s", id, name); }
就可以用统一的show(p)“函数”输出Teacher或者Student结构体变量的id、name属性了。
当然,还有一种方法是GTK里使用的,类似C++的思想,用一个统一的结构体以及一个类似“构造函数”的函数进行类型转换(就是对应成员的复制)实现继承、多态等……
这个有点像面向对象编程……
……
Dec 30, 2010 04:54:50 PM
早期的计算机语言基本上都有“宏”的痕迹,也就是字面替换。像 TeX、MetaPost 之类的东西,属于完全意义上的宏语言。宏语言对于逻辑能力较好同时又有些艺术细胞的人而言,可以构造出很优雅的代码。但是,工程上的东西的确不适合使用宏,因为太容易出错了。所以 c++ 的书一般会告诉用户『宏是邪恶的』。
Dec 31, 2010 02:34:23 AM
@Garfileo: 嗯,宏定义有时的确很有用,但有时用不好会出问题。正在学习中……