关于#define预处理的讨论

Plux posted @ 2010年12月29日 18:36 in C/C++ with tags c/c++ #define 宏定义 , 2488 阅读

以前看过一本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++的思想,用一个统一的结构体以及一个类似“构造函数”的函数进行类型转换(就是对应成员的复制)实现继承、多态等……

这个有点像面向对象编程……


……

Avatar_small
Garfileo 说:
Dec 30, 2010 04:54:50 PM

早期的计算机语言基本上都有“宏”的痕迹,也就是字面替换。像 TeX、MetaPost 之类的东西,属于完全意义上的宏语言。宏语言对于逻辑能力较好同时又有些艺术细胞的人而言,可以构造出很优雅的代码。但是,工程上的东西的确不适合使用宏,因为太容易出错了。所以 c++ 的书一般会告诉用户『宏是邪恶的』。

Avatar_small
Plux 说:
Dec 31, 2010 02:34:23 AM

@Garfileo: 嗯,宏定义有时的确很有用,但有时用不好会出问题。正在学习中……


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter