《嵌入式C编程:PIC单片机和C编程技术与应用》一第3章预编译指令3.1 标准预编译指令...

    xiaoxiao2024-03-17  18

    本节书摘来自华章出版社《嵌入式C编程:PIC单片机和C编程技术与应用》一书中的第3章,第3.1节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问云栖社区“华章计算机”公众号查看

    第3章

    Embedded C Programming: Techniques and Applications of C and PIC MCUS

    预编译指令

    编译器对C程序的处理可以明确地分为两步。第一步由预编译器完成。以#开头的预编译指令可能会影响编译器设置或者进行文本替换。注意,预编译器变量(标识符)和正常的C变量是不一样的。预编译完成之后,编译器将不会看到任何预编译指令或标识符。下面介绍常见的预编译指令。

    3.1 标准预编译指令

    3.1.1 #define id text

    id表示定义的名字text表示替换的文本使用时,程序中所有出现id的地方都会被替换成text。#define只做简单的文本替换。图3-1给出了#define的用法。这些#define称为宏。

    第一个define是很经典的用法,定义了一个常量,一般情况下,该常量在程序中很重要并且需要容易修改,或者在代码中出现了很多次。注意,数字15后面的注释。在预编译器处理代码之前,注释会被移除,否则后面代码中的;i=i+1){将会被当作注释而忽略掉。第二个define使用了e3.h中定义的PIN_C6(#define PIN_C6 31766)。这两次替换都会在预编译期间完成。第三个和第四个define演示了如何在define中使用语句,也演示了如何在一个define中使用其他define定义的内容。在第六个define中,使用了数学算式,将其用小括号括起来是个好习惯。如果不这么做可能会造成不可预知的结果。例如,下列这些常量及其应用:

    在预编译之后,会变成100 - 1×3,和预期的(100 - 1)×3完全不同。 预编译器并不了解C语言,它只是做简单的文本替换。这样可能会对错误分析造成一些困扰。假设在第六个define中,我们把乘号*错写成;,在编译时,下面这行会提示错误:

    本来这一行并没有问题。然而,由于这行使用了第六个输入错误的宏定义,在预编译时也被替换过来,结果就出现了错误提示。 注意,对于变量i,我们使用的是小写字母,而#define则使用的是全大写。这就是所谓的编程风格或者代码标准,可以方便区分#define符号。 预编译指令由#开始,占用一整行,但有时也需要占用多行。为了达到这个目的,需要使用反斜线。请看下面的例子:

    这种形式的#define看起来有点像函数调用。还有一种叫类函数宏或者带参数的宏,将在第13章中详细介绍。 编译器预定义了一些宏,例如: date__和__time 它们分别定义了编译的日期和时间。

    3.1.2 #include 或#include “filename”

    在第1章中已经讲过#include。总的来说,它会将文件的内容插入代码中相应的位置。第一种形式中的<>表示编译器会首先在预定义的include文件目录中搜索文件,使用第二种形式""则会先在工程目录中搜索文件。<>通常用于包含编译器支持的文件,而""则用于工程相关的文件。无论哪种方式,都可以使用全路径:

    但是在删除工程文件时,这种做法会比较麻烦。 在IDE中,编译器的命令行或.ini配置文件都可以指定文件搜索目录列表。 每个工程中通常都会加入设备文件(如16F887.h)。例如,本书的例子中会加入e3.h这个设备文件。在include文件中还可以继续包含其他文件。

    3.1.3 #ifdef、#ifndef、#else、#endif和#undef

    ifdef是一种条件编译指令。除非在前面用#define定义过这个标识符,否则#ifdef和#endif之间的部分会被编译器忽略。例如:

    我们经常会看到类似的代码,只有当代码中存在下面这行时才会执行其中的打印操作。

    注意,这里在标识符(DEBUG)之后没有任何文字。因为这种情况下我们不需要为DEBUG指定任何值。#else通常用在下列情况下:

    ifdef是在编译阶段而不是在运行时起作用的,理解这一点非常重要。因此,上面的代码中只有一行reading=会被编译并放到芯片的内存中。在编译阶段已经决定了要编哪一行。这是个非常强大的工具,它让一份源代码只需要经过简单的配置就可以生成多个应用程序。#ifndef正好相反,当标识符没有定义的时候,会编译#ifndef之后的代码。#undef用来取消之前#define定义的标识符。

    3.1.4 #if、#else、#elif和#endif

    if和#ifdef类似,但#ifdef用来检测标识符有没有定义,而#if用来判断表达式是不是为真(非0)。标识符必须是宏而不是C变量。下面的例子中还用到了可选的#elif(else if):

    第5章将会详细介绍表达式。现在只需要知道==是用来测试是否相等的运算符,单个等号=是赋值运算符。

    3.1.5 #error

    编译程序时,只要遇到#error就会生成一个编译错误提示消息并停止编译,其后面跟的字符不用双引号包围,可以用来判断某段代码是否被编译过。使用#if和#include时,有时你认为应该编译的代码却没有编译,将#error放到应该被编译的代码中,如果没有抛出错误,那就说明这段代码被编译器忽略了。其中一个原因可能是什么地方少了一个#endif。很难发现#include文件中是否少一个#endif,因为被编译器忽略的代码在#include之后,两者不是同一个文件。通常的用法如下:

    如果某些宏的设置不对,导致#if的条件成立,编译就会停止。 在某些编译器中,如CCS C编译器,#error后面的文本会被预编译器替换。 例如,对于前面的#define示例,下面这行代码将会抛出下列错误(GREEN_LED的定义在e3.h中):

    3.1.6 #nolist和#list

    nolist用来告诉编译器不要将这行输出到list文件(.lst)中。#list用来恢复正常操作。LST文件由编译器创建,用来显示每行C代码生成的汇编代码。这条预编译指令可以用来防止过长的注释或者数据定义在list文件中占用太多空间。编译器设备头文件就是用这种方法防止所有设备相关的#define出现在list文件中。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)