宏定义

学过 C 语言的朋友们肯定知道

  • 宏可以定义常量
  • 宏可以定义代码块

但是大家真的了解宏了吗?我给大家分析分析。

我们应该知道:

  • #define 是预处理器处理的单元实体之一
  • #define 定义的宏可以出现在程序的任意位置
  • #define 定义之后的代码都可以使用这个宏

来谈谈最开始我们说的定义宏常量吧。

  • #define 定义的宏常量可以直接使用
  • #define 定义的宏常量本质为字面量

怎么理解?
所谓字面量,其不占内存单元,没有存储空间。这与 const 定义的常变量有着巨大的区别。

那宏到底做了什么?我们来看看下面的几个宏常量定义,看看他们正不正确。

//宏定义
#define ERROR -1
#define MPATH1 "./test/a.c"
#define MPATH2 ./test/a.c
#define MPATH3 ./test/\
    a.c
    
int main()
{
    int error = ERROR;
    char* s1 = MPATH1;
    char* s2 = MPATH2;
    char* s3 = MPATH3;

    return 0;
}

既然宏是被预处理器所处理,想要知道我们的宏定义的对不对,就要让预处理器告诉我们。

单步编译
在这里插入图片描述
这说明我们的宏定义是正确的。

大家可能会纳闷,这样的代码直接编译可执行文件时会出错,但是这里单步编译却没错,为什么?

其实很简单,看看单步编译后的文件就知道了。
在这里插入图片描述在这里插入图片描述
预处理阶段预处理器只是简单的将宏做了文本替换,不做语法检查。那谁来做语法检查?编译器呗。


我们来看看宏定义的表达式

  • #define 表达式的使用类似函数调用
  • #define 表达式可以比函数更强大
  • #define 表达式比函数更容易出错

看看下面的宏表达式正确吗?

#include<stdio.h>
#include<stdlib.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 arr[10] = {0};
    printf("sum = %d\n",__SUM_(3,9));
    printf("min = %d\n",__MIN_(3,9));
    printf("dim = %d\n",__DIM_(arr));

    return 0;
}

结果
在这里插入图片描述
我们通过宏求出了两个数的和,两个数中的最小值,还得到了一个数组的数组大小。是不是觉得宏挺强大的,尤其是求数组大小。

看到这大家应该都很兴奋了吧,你们还记得我刚才说过的话吗,宏可以比函数强大,但是也比函数更容易出错。

看看修改后的代码

#include<stdio.h>
#include<stdlib.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 arr[10] = {0};
    printf("sum = %d\n",__SUM_(3,9));
    printf("min = %d\n",__MIN_(3,9));
    printf("dim = %d\n",__DIM_(arr));


    int tmp1 = 1;
    int tmp2 = 3;

    //本意想求 (1+2)的平方
    int a = __SUM_(1,2) * __SUM_(1,2);
    //本意想求 tmp1 和 tmp2 两者间的最小值,求完值后让tmp1++
    int b = __MIN_(tmp1++,tmp2);

    printf("a = %d\n",a);
    printf("b = %d\n",b);

    return 0;
}

运行结果
在这里插入图片描述
好像a和b的值不对啊,wtf。
别急,老规矩,看看中间文件
在这里插入图片描述
问题找到了,预处理器在预处理的时候进行了文本替换,但是很显然他不理解我想干什么,本来我想先求1+2的值再相乘的,这里变成了先算乘法了。求最小值的那里tmp++做了两次啊。

原来宏表达式可以这么坑。当然,合理的使用宏表达式可以很好的帮助我们,至于这些坑嘛,我们只能规范自己来避免。


注意!!!
宏表达式不能递归定义

// error
#define __SUM_(n) ((n>0)?(__SUM_(n-1)+n):0)
  • 宏表达式中不能出现递归定义
  • 宏表达式被预处理器处理,编译器不知道宏表达式的存在
  • 宏表达式用“实参”完全替代形参,不进行任何运算
  • 宏表达式没有任何的“调用”开销

前面说了这么多,我们来讨论一下 C 语言中常讨论的问题,作用域。

宏定义有作用域限制吗?

看看下面的代码合法吗?

#include<stdio.h>
#include<stdlib.h>

void func()
{
    #define a 100
    #define __SUM_(a,b) (a)+(b)
}

int main()
{
    int i = __SUM_(a,20);
    printf("%d\n",i);

    return 0;
}

运行结果
在这里插入图片描述
这是不是说明,即使是在 void func() 函数中定义的宏,在 main 中也能使用。

我们把函数位置调换一下

#include<stdio.h>
#include<stdlib.h>

void func();

int main()
{
    int i = __SUM_(a,20);
    printf("%d\n",i);

    return 0;
}

void func()
{
    #define a 100
    #define __SUM_(a,b) (a)+(b)
}

编译过不了了。为什么?

其实宏在定义之后,后面的代码就可以使用这个宏了。作用域的概念是针对变量和函数的,他不针对宏,为什么啊?因为宏是预处理器处理的,编译器根本不知道宏标识符的存在,所以编译器不能把作用域的概念运用于宏。


C 语言中一些强大的内置宏

//注意,是两个下划线,左边两个右边也两个
__FILE__        被编译的文件名
__LINE__        当前行号
__DATE__        编译时的日期
__TIME__        编译时的时间
__STDC__        编译器是否遵循标准C规范

番外

#include<stdio.h>
#include<stdlib.h>

#define MALLOC(type,n) (type*)malloc(sizeof(type)*n)
#define FREE(p) (free(p),p=NULL)

#define FOREACH(i,m)  for(i = 0;i<m;i++)
#define BEGIN {
#define END   }

int main()
{
    int count = 10;

    int* arr = MALLOC(int,10);
    if(arr){
        int i;
        FOREACH(i,count)
        BEGIN
            arr[i] = i+1;
        END

        FOREACH(i,count)
        BEGIN
            printf("arr[%d] = %d\n",i,arr[i]);
        END

        FREE(arr);
    }
    if(!arr){
        printf("arr now is NULL\n");
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值