C语言宏定义使用方法和注意事项

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


猜你喜欢

转载自blog.csdn.net/liuchunjie11/article/details/80484400
今日推荐