当你调用一个过程既被声明成函数又被声明成宏时,你可以用如下两种方法来强制编译器使用函数或宏(编译器默认会使用宏)。
1、使用强制类型转换
#include <ctype.h>
a = toupper(a); //使用宏
a = (toupper)(a); //使用函数,因为toupper被强制转换成函数指针。
2、使用#undef
#include <ctype.h>
#undef toupper //注意这里是小写,以后调用过程toupper都为函数调用,因为宏定义已被//取消。
那么,我们应该怎样来选择在一个程序中使用宏还是使用函数调用呢?
1、时间VS 大小
使用宏时,因为宏被就地展开,因此,用宏编译出来的代码会比用函数编译出来的代码大。但宏展开后没有了函数的参数传递,所以运行起来比函数要快。
使用函数时因函数只是被多次执行,因此代码小,但函数调用时增加了开销。
2、函数可以通过函数指针来调用,而宏则不行。
如:
int AddOne(int a) {++a}; //定义一个函数
int (*plusOne)(int a); //定义一个函数指针
pluseOne = Addone; //初始化函数指针
(*pluseOne)(2); //通过函数指针调用函数
3、在多任务并发执行的应用程序中,使用函数时需考虑函数的重入问题。宏因为就地展开,因此,不用考虑重入问题。
4、使用宏时,要注意宏的参数有可能出错。
a) 宏定义不当出错
如:
宏定义
#define F (x) (x+1)
现在假如有一个如下所示的宏调用:
F(1)
预处理器展开它,出现下面不希望的情况:
(x) (x+1) (1)
出现这个问题是因为在宏定义中F和括号之间存在空格,当这个空格取消后,调用宏时可以有空格空隙,像下面的调用:
F (1)
依然可以正确地展开为:
(1+1)
b) 当宏传递参数时出错
如:优先级不同出错
#define FLOOR(x,b) x>=b ? 0:1
现在假如用表达式作参数:
if(FLOOR(a&0x0f,0x07))
宏将展开成:
if(a&0x0f>=0x07 ? 0 : 1)
因为&的优先级比>=的低,所以宏的展开结果将会使我们惊讶。一旦发现这个问题,可以通过在宏定义内的各个地方使用括弧来解决,(这是创建预处理器宏时使用的好方法。)上面的定义可改写成如下:
#define FLOOR(x,b) ((x)>=(b) ? 0 : 1)
c) 当一个宏多次使用参数时出错
如:
#define toupper(c) ((islower(c)) ? _toupper(c) : (c))
#include <ctype.h>
int a = ‘m’;
a = toupper(a++);
程序运行后,a的值会加2;
上面的程序被编译器展开后:
int a = ‘m’;
a = islower(a++) ? _toupper(a++) : (a++);
由此可见,变量a被增加了两次。
5、类型检查
当定义为函数时,编译器会检查函数的参数,但定义为宏时,编译器只检查参数的个数。
6、使用内联函数(C++或支技内联函数的编译器中)