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

本节书摘来自华章出版社《嵌入式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文件中。

时间: 2024-08-01 08:57:14

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

《嵌入式C编程:PIC单片机和C编程技术与应用》一1.8 时间

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第1章,第1.8节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 1.8 时间 在delay_ms中,ms表示毫秒(milliseconds).编程中常用的时间单位有: ns ?nanosecond(纳秒) ??0.000?000?001s μs ?microsecond(微秒) 0.000?001s ms millisecond(毫

《嵌入式C编程:PIC单片机和C编程技术与应用》一第1章C语言概述和程序结构1.1 C源代码

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第1章,第1.1节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 第1章 Embedded C Programming: Techniques and Applications of C and PIC MCUS C语言概述和程序结构 1.1 C源代码 下面是一段C语言源代码: 这段代码初看起来可能会觉得难以理解,但读完本书并完成书中

《嵌入式C编程:PIC单片机和C编程技术与应用》一1.4 C预编译指令

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第1章,第1.4节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 1.4 C预编译指令 预编译是C语言中一个非常有意思的特性.预处理使用工具(预处理器)在编译前先扫描一遍代码,并对代码做出相应的修改从而生成用来编译的代码.预编译指令由#开始,占用一整行.在第3章中将会详细介绍它.在上面的例子中,#include指令将文件(e3.h)的

《嵌入式C编程:PIC单片机和C编程技术与应用》一1.5 函数

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第1章,第1.5节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 1.5 函数 接下来,我们看到了一个函数定义"main".所有程序都需要有一个main()函数,也就是主函数.这是程序开始执行的地方.在本书中,当提到函数名时,我们都会在它的名字后面跟上(),表示这是个函数. 函数名前的void表示此函数的类型是vo

《嵌入式C编程:PIC单片机和C编程技术与应用》一3.6 测验

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第3章,第3.6节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 3.6 测验 1. 将下列代码放入一个函数,将会发生什么事情? (a)程序将会延时5s (b)变量x被赋值为5 (c)第2行出现语法错误 (d)第3行出现语法错误 (e)什么都不会发生 2. 在练习X3-6.c中,整个文件只使用了一个分号,显示了预编译器的强大功能.下列

《嵌入式C编程:PIC单片机和C编程技术与应用》一2.2 C常量语法

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第2章,第2.2节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 2.2 C常量语法 2.2.1 二进制 需要多位(bit)二进制数才能表示一个大于1的值,具体需要多少位,则取决于数字大小.一个8位的二进制数(一个字节)可以表示256个数字(0-255). 16位的二进制数可以表示从0到65?535的值.如果使用一个字节来传输信息,一

《嵌入式C编程:PIC单片机和C编程技术与应用》一1.6 声明

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第1章,第1.2节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 1.6 声明 "int i"声明了一个以标识符i作为变量名的数据类型.int表示该变量是一个整数.本例中,i只能在main()函数中使用.如果声明i的这一行放到函数之前(在函数外),那么i也可以在其他函数中使用.变量的使用范围称作变量作用域.作用域将

《嵌入式C编程:PIC单片机和C编程技术与应用》一1.7 语句和表达式

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第1章,第1.7节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 1.7 语句和表达式 在上面的例子中for那一行就是一个语句(statement).程序在运行时会执行这些语句.该语句包含3个表达式.表达式将在第5章介绍,语句将在第6章介绍.在for语句中: 在这个例子中,for语句的后面有4行语句放在一对大括号{}内,随着i从1增长

《嵌入式C编程:PIC单片机和C编程技术与应用》一2.8 测验

本节书摘来自华章出版社<嵌入式C编程:PIC单片机和C编程技术与应用>一书中的第2章,第2.8节,作者 [美]马克·西格斯蒙德(Mark Siegesmund),更多章节内容可以访问"华章计算机"公众号查看 2.8 测验 存储下面的二进制数需要多少字节? 1001011001 (a)10 (b)1 (c)1.2 (d)2 (e)二进制数无法用字节存储 下列哪个选项可以用来表示一个十六进制数? (a)两个十进制数 (b)字节 (c)半字节 (d)一个八进制数 (e)三个二进制