C语言宏定义使用方法和注意事项
一、总结
1、预处理器直接对宏进行文本替换
2、宏使用时的参数不会进行求值和运算
3、预处理器不会对宏定义进行语法检查,宏定义时出现的语法错误只能被编译器检测
4、宏定义的效率高于函数调用(宏不占用内存,函数占用内存)
5、宏的使用会带来一定的副作用
6、#define定义的宏可以出现在程序的任意位置,#define定义之后的代码都可以使用这个宏
7、#define 表达式的使用类似函数调用,但是宏比函数功能更强大,比函数更容易出错
8、宏表达式中不能出现递归定义
二、代码测试(ubuntu 10 gcc平台)
1、代码:
#include <stdio.h> #define ERROR -1 #define PATH1 "D:/test/test.c" #define PATH2 D:/test/test.c #define PATH3 D:/test/ //下面这个递归是不对的,宏表达式中不能出现递归定义 #define SUM(n) (n > 0 ? SUM(n - 1) + n : 0) #define _SUM_(a,b) (a) + (b) #define _MIN_(a,b) ((a) < (b) ? (a) : (b)) #define _DIM_(a) sizeof(a)/sizeof(*a) //记住用define定义的宏,预处理之后全都消失 //所以下面这个函数实际是空的,预处理后函数内容都是空的 void def(void) { #define PI 3.1415926 #define AREA(r) r*r*PI } double area(double r) { return AREA(r); //这个预处理后变成:return r*r*3.1415926; } int main() { int err = ERROR;//预处理通过,编译通过 char* p1 = PATH1; //预处理通过,编译通过 char* p2 = PATH2; //预处理通过,但是编译不通过,因为预处理不进行语法检测,编译要进行语法,语句,语义检查,这里没有加双引号,导致错误 char* p3 = PATH3; //预处理通过,但是编译不通过,因为预处理不进行语法检测,编译要进行语法,语句,语义检查,这里没有加双引号,导致错误 double r = area(5); int a = 1; int b = 2; int c[4] = {0}; int s1 = _SUM_(a,b); int s2 = _SUM_(a,b)*_SUM_(a,b);//这里预处理好后变成:int s2 = (a) + (b)*(a) + (b); 所以是1 + 2*1 + 2 = 5 int m = _MIN_(a++,b); //这里预处理后变成这样:int m = ((a++) < (b) ? (a++) : (b));所以a执行了两次++,最后变成3 int d = _DIM_(c); int su = SUM(5); //预处理通过,编译不通过,因为宏定义不能出现递归 printf("a = %d\n",a); //a = 3 printf("s1 = %d\n",s1); //s1 = 3 printf("s2 = %d\n",s2); //s2 = 5 printf("m = %d\n",m); //m = 2 printf("d = %d\n",d); //d = 4 printf("r = %lf\n",r); //r = 78.539815 return 0; }
我们先看下预处理之后是什么东西(预处理命令在终端输入:gcc -E test.c -o test.i 然后找到这个文件用gedit打开就行了)
void def(void) { } double area(double r) { return r*r*3.1415926; } int main() { int err = -1; char* p1 = "D:/test/test.c"; char* p2 = D:/test/test.c; char* p3 = D:/test/; double r = area(5); int a = 1; int b = 2; int c[4] = {0}; int s1 = (a) + (b); int s2 = (a) + (b)*(a) + (b); int m = ((a++) < (b) ? (a++) : (b)); int d = sizeof(c)/sizeof(*c); int su = (5 > 0 ? SUM(5 - 1) + 5 : 0); printf("a = %d\n",a); printf("s1 = %d\n",s1); printf("s2 = %d\n",s2); printf("m = %d\n",m); printf("d = %d\n",d); printf("r = %lf\n",r); return 0; }
再看下终端输出
代码里面都有注释,讲的很清楚,所以不讲了
2、代码2 (注意:这个代码里面有bug,看看你们能不能发现)
#include <stdio.h> #include <malloc.h> #define MALLOC(type,l) (type *)malloc(sizeof(type) * l) #define FREE(p) (free(p),p = NULL) #define LOG(s) printf("[%s] {%s:%d} %s \n",__DATE__,__FILE__,__LINE__,s) #define FOREACH(i,m) for(i = 0;i < m;i++) #define BEGIN { #define END } int main() { int x = 0; int* p = MALLOC(int,5); LOG("Begin to run main code ..."); FOREACH(x,5); BEGIN p[x] = x + 1; END FOREACH(x,5); BEGIN printf("p[%d] = %d\n",x,p[x]); END FREE(p); LOG("END"); return 0; }
这么看是不是有点复杂,那我们预处理下,在终端输入gcc -E test.c -o test.i 然后在当前文件夹下面找到这个新生成的test.i,然后双击在gedit里面把它打开
看出来没!是不是第1个for后面有个冒号,这是第一个bug,同样下面for也是有个冒号,这是第二个bug
那这个bug会导致什么问题么?加了冒号,for循环什么都不执行,for循环执行完x = 5,下面p[5] = 5 + 1;
但是我们申请内容只申请了5个int类型数据,但是现在赋值到第6个了,所以会导致错误,具体什么现象,你们在终端里面直接运行就知道了,所以我们把这个bug去掉,然后编译运行下,看下终端输出:
参考资料《狄泰软件C语言进阶教程》