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语言进阶教程》