vlambda博客
学习文章列表

KEIL编译器预处理器和预定义宏(include,ifdef,def等)

 原创文章,欢迎分享,转载请注明来源,并给出原始链接,未经书面允许,请勿用于商业用途。

Keil使用的C编译器支持常用的编译器预定义宏和对文件进行预处理。

在设置界面打开预处理器选项,就可以输出每一个文件对应的预处理文件。当然,你不选择这个选项,编译器也会默认启用预处理编译的,只不过不会对应每一个C文件生成这个预处理结果文件而已。

首先,我们了解一下何为预定义宏和预处理器以及他们起什么作用

预定义宏:ARM编译器预定义了很多宏,这些宏提供了工具链版本号和编译器选项的相关信息。

预处理器:对每一个文件的预处理命令进行预先处理(不是编译),包括包含文件的展开,常量的计算,重复定义的排除等等,简而言之,就是把一个C文件里面包含或者应用的其他文件的信息整合到一个文件,并进行一定的处理。

一,预定义宏

常见的预定义宏如下:

__FILE__:  被编译文件的名称

__LINE__:被编译文件的当前行号

__DATE__:  编译开始的日期

__TIME__:  编译开始的时间

__FILE__,__LINE__宏我们一般用来输出调试信息,以便于检查输出文件的信息位置,其值是一个字符串。

例如:printf(“File:%s,Line:%s,value=%d\n”, __FILE__,__LINE__,number);

通过这个信息输出,我们就清楚的知道,在哪一个文件哪一行,变量number此时的数值是多少。

__DATE__,__TIME__宏,一般组合起来使用,输出一个工程编译信息。

例如:printf(“File:%s,Line:%s \n”, __ DATE __,__ TIME __,);

通过这个信息输出,我们就知道该文件是什么日期和时间编译的。

其他的很少用得到的可以查询KEIL的帮助文档:



二,预处理器

预处理器的作用是处理源文件中发现的命令。

预处理器命令必须放在一行的开头。所有命令都以‘#’为前缀。例如

#include<stdio.h>

#pragma

#define LedOn()             1

 

常见的预处理命令如下:

define    :定义一个预处理器宏或常数
if            :计算一个条件编译的表达式
elif         :当前面的if ifdef ifndef elif分支没有执行发起一个if条件的选择分支
else        :当前面的if ifdef ifndef分支没有执行 发起一个选择分支
endif     :结束一个if ifdef ifndef elif else
error     :输出一个由用户定义的错误信息 本命令指示编译器输出指定错误信息
ifdef      :计算一个条件编译的表达式 计算的参数是一个定义的名称
ifndef     :和ifdef相似 但如果没定义则计算成立
include :从一个外部文件中读源文本,符号序列决定了包含文件的查找顺序,编译器在包含文件目录中查找用大于/小于号“< >”指定的包含文件,在当前目录中查找用双引号“”指定的包含文件
line        :指定一个行号和一个可选的文件名,这用在错误信息中来标识错误的位置

pragma  : 允许指定可包含在命令行的命令 程序可以在命令行中包含相同的命令
undef   : 删除一个预处理器宏或常数定义

我们用下面的一段代码分别演示上面的预处理命令的作用:

#ifndef    __PREPROCESSOR_H__  //判断是否定义了一个宏名称__PREPROCESSOR_H__#define __PREPROCESSOR_H__  //如果没有定义,就定义这个宏#include         <stdio.h>   //包含一个系统头文件
#undef TRUE//如果在其他地方定义了这个宏,则取消其定义。但是在这之前的代码使用之前的定义#undef FALSE
#ifndef TRUE #define TRUE         1       //因为前面已经取消了宏TRUE的定义,这里定义的宏对后面的代码开始生效。 #define FALSE                     0#endif
#if TRUE == 1  #error "TRUE==1"         //因为TRUE被定义为1,所以这里会发生一个编译错误。一般用来检测不应该发生的定义错误发生了。#elif TRUE == 0   #error      "TRUE = 0"#endif#pragma pack(push)    //存储当前pragma状态#pragma pack(1)      //告诉编译器下面的结构按照字节压缩保存.              //如果不使用这个定义,下面的结构大小12字节,使用后11字节              //压缩后存储空间变少,但是访问变量的指令会变复杂。typedef struct{         int mode;         unsigned char hour;         unsigned char minute;}ALARMITEM;#pragma pack(pop)      //恢复pragma状态

熟练的掌握这些宏和预处理的使用方法,对我们管理大型项目的文件和灵活运用编译信息将起到非常好的作用。下一章我们再仔细的讲一讲起运用技巧。