6、正确的使用宏

一、c语言中的宏定义

1、基本概念

  1. #define是预处理器处理的单元之一
  2. #define定义的宏可以出现在代码中的任何位置
  3. #define定义之后的代码都可使用该宏

2、本质

#define定义的宏常量本质上是一个字面量,下面我们用一个例子来了解他的基本概念和本质。
注意下面没有加标准输入输出的头文件。

#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c
int main()
{
    int err = ERROR;
    char* p1 = PATH1;
    char* p2 = PATH2;
    char* p3 = PATH3;
}

下面我们在linux下对该代码做预处理。

gcc -E test.c -o test.i

此时可以顺利通过预处理,test.i的文件如下

int main()
{
    int err = -1;
    char* p1 = "D:\test\test.c";
    char* p2 = D:\test\test.c;//可以看到直接替换
    char* p3 = D:\testtest.c;//可以看到上面的/被test.c替换了
}

此时我们再完整编译该代码

gcc test.c

显然这里会是出错的,为什么呢?因为这里的将一个不合法的表示赋值给了一个指针。所以也就是说使用宏定义有它的好处也有它的缺点。下面来具体的讨论。

二、如何正确的使用宏

1、优点

①可以类似于函数调用来使用
②#define 表达式在某些情况下比函数更强大,如我们可以直接计算出数组的长度

2、缺点

有可能会导致难以发现的错误。

3、具体例子如下

#include <stdio.h>

#define _SUM_(a, b) (a) + (b)
#define _MIN_(a, b) ((a) < (b) ? (a) : (b))
#define _DIM_(a) sizeof(a)/sizeof(*a)

int main()
{
    int a = 1;
    int b = 2;
    int c[4] = {0};
    int s1 = _SUM_(a, b);
    int s2 = _SUM_(a, b) * _SUM_(a, b);
    int m = _MIN_(a++, b);
    int d = _DIM_(c);
    printf("s1 = %d\n", s1);
    printf("s2 = %d\n", s2);
    printf("m = %d\n", m);
    printf("d = %d\n", d);
    return 0;
}

同样地我们也可以对该代码做预处理,注意此时可以将stdio.h注释掉

gcc -E test.c -o test.i

可以看到主函数如下:

int main()
{
    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);
    printf("s1 = %d\n", s1);
    printf("s2 = %d\n", s2);
    printf("m = %d\n", m);
    printf("d = %d\n", d);
    return 0;
}

然后同样的我们可以进行完整的编译,看看效果是不是如我们所预期的那样。
在这里插入图片描述
s2应该为9,但是结果却为3,具体为什么会这样,大家自己分析就可以了,而对于m=2,是由于a++了两次,但是事实上我们只需要+一次就可以了,所以这是它的缺点所在,但是我们却非常的方便的计算出了数组的长度。

三、宏表达式和函数的对比

1、基本了解

(1)宏表达式被预处理器处理,编译器不知道它的存在,而预处理器是不进行语法的检错的。
(2)函数的调用需要涉及到堆栈问题,因此需要资源开销,而宏表达式不需要。
(3)宏表达式中不能出现递归调用,如下面的式子是错误的。
(4)作用域上的区别。

#define _SUM_(n)	((n>0)?(SUM(n-1)+n):0)
int s=_SUM_(10);
#include <stdio.h>
void def()
{
    #define PI 3.1415926
    #define AREA(r) r * r * PI
}

double area(double r)
{
    return AREA(r);
}
int main()
{
    double r = area(5);
    printf("PI = %f\n", PI);
    printf("d = 5; a = %f\n", r);
    return 0;
}

从作用域的角度出发,该程序是不合法的,但是事实上是可以通过编译的,因为宏定义之后的代码都可以使用它。所以这点和函数于变量的调用也是有区别的。下面是一些内置的宏,可以直接调用。

在这里插入图片描述

2、综合应用

#include <stdio.h>
#include <malloc.h>

#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)

#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;
    END
    
    FOREACH(x, 5)
    BEGIN
        printf("%d\n", p[x]);
    END
  
    FREE(p);
    LOG("End");  
    return 0;
}

运行结果如下:
在这里插入图片描述
该例程的宏定义在使用上都比函数要优越,且有些功能是无法通过函数来去实现的,很具有参考性。总的来说,宏定义的效率高于函数调用,但是使用不当也会带来很大的副作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值