首页 /  技术专区  /  C 宽屏模式 >

预定义宏与调试宏

1 预定义宏

__DATE__、__FILE__、__LINE__、__TIME__、__func__。

其实预定义宏不止这些,不过这里我们就只介绍这些。

1.1 什么是预定义宏

预定义宏,也可以称为编译器内置宏,这个宏并没有定义在哪个.h文件中,所以不能再哪个.h中找到这些玩意。

进行预编译时,当预编译器看到这些玩意时,会自动处理这些预定义宏。

其实将这些预定义宏称为预编译关键字,可能更好些。

1.2 作用

(1)__DATE__:代表预处理的日期

        当预处理器检测到__DATE__后,会将其替换为"月 日 年"的字符串形式的时间,时间格式是西方人习惯的格式。

(2)__FILE__:代表当前预编译正在处理的那个源文件的文件名

        当预处理器检测到__FILE__后,会将其替换为"***.c"的文件名。

(3)__LINE__:代表__LINE__当前所在行的行号

        当预处理器检测到__LINE__后,会将其替换为__LINE__当前所在行的行号(整形)。

(4)__TIME__:代表对源文件进行预编译时的时间

        当预处理器检测到__TIME__后,会将其替换为“hh:mm:ss”格式的时间。

(5)__func__:当前__func__所在函数的函数名 

        不过这个在预编译阶段不会被处理,而是留到编译阶段处理。

(6)例子

helloworld.c

#include <stdio.h>

int main(void)
{       
    printf("预编译的日期:%s\n", __DATE__);
    printf("预编译的文件:%s\n", __FILE__);
    printf("当前所在行号:%d\n", __LINE__);
    printf("预编译的时间:%s\n", __TIME__);
    printf("当前所在函数:%s\n", __func__);
    return 0;
}

查看预编译结果:

int main(void)
{
     printf("预编译的日期:%s\n", "Jul  5 2018");  //替换为了日期
     printf("预编译的文件:%s\n", "helloworld.c"); //替换为了文件名
     printf("当前所在行号:%d\n", 7);  //替换为了__LINE__所在的行号
     printf("预编译的时间:%s\n", "02:21:08"); //替换为了预编译的时间
     printf("当前所在函数:%s\n", __func__);//没有替换,到第二阶段编译时再处理,__func__代表的函数名是main
     return 0;
}

打印结果:

预编译的日期:Jul  5 2018
预编译的文件:helloworld.c
当前所在行号:7
预编译的时间:02:28:15
当前所在函数:main

除了__func__外,其它几个都可以定义相应的变量来存放,比如。

int line = __LINE__;
char date[] = __DATE__;

1.3 预定义宏的意义 与 调试宏

(1)意义

        常常用于调试打印、跟踪代码用。

        当一个程序写大了后,在调试程序的功能性错误时,往往需要打印信息来跟踪代码,看看程序是运行到什么位置时才出现了功能性错误,以方便我们调试。

printf("%s %d %s\n", __FILE__, __LINE__, __func__);

        当然如果不需要显示文件名和函数名的话,其实直接打印行号就可以了。

printf("%d\n", __LINE__);

(2)调试宏

        在每个需要打印的地方都写printf会非常的麻烦,因此我们可以把它写成调试宏。

1)例子:

#include <stdio.h>
//调试宏,DEBUG的名字可以自己随便起
#define DEBUG printf("%s %d %s\n", __FILE__, __LINE__, __func__);

void exchange(int *p1, int *p2)
{      
    DEBUG
    int tmp = 0;
    DEBUG
    tmp = *p1;
    DEBUG
    *p1 = *p2;
    DEBUG
    *p2 = tmp;
    DEBUG
}

int main(void)
{       
    int a = 10; 
    int b = 30; 
    DEBUG
    exchange(&a, &b);
    DEBUG
    printf("a=%d, b=%d\n", a, b);
    DEBUG
    return 0;
}

通过打印信息来跟踪程序,其实有些时候比“单步运行调试”更好用,因为单步运行调试在某些情况其实很麻烦,不如打印信息来的好使。

2)如果你想打印自定义信息的话,我们还可以将调试宏定义为带参宏

#define DEBUG(s1, s2) printf(s1, s2);

在程序中使用时,可以指定你要任何打印的调试信息。

DEBUG("%d\n", va); //你想写什么,可以由你自己决定。
DEBUG("%s\n", str);

疑问:感觉这也不比直接写printf("%d\n", va)更方便呀?

不直接使用printf,而是写成DEBUG(s1, s2)带参宏的形式,可以方便我们使用“条件编译”来快速打开和关闭调试宏,后面将再介绍这个问题。



头像
0/200
图片验证码