c语言编译与预处理命令
所谓预处理,是指源文件在进行编译的第一遍扫描之前所作的工作,由预处理程序完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分进行处理,处理完毕自动进入对源程序的编译。
在前面的章节中已多次出现了#define与#include,在C语言源程序中这些以“#”开头的命令都放在函数之外,而且一般都放在源文件的前面,这些都是预处理指令。
变量式宏定义也就是不带参数的宏定义,其定义的一般形式为:
#define 标识符 字符串
例如在程序中需要经常使用圆周率完成一定的功能,这个时候,当然可以反复使用3.1415926这个浮点数穿插到程序中,但是书写过于频繁,可能会因为疏忽发生错误,如果程序在更新过程要求提高或者降低圆周率的精度,那需要修改程序的工作量是相当大的,怎么办才能简化操作呢?这里,就可以使用变量式宏定义,具体如下:
#define PI 3.1415926
这个宏定义的作用是:使用标识符PI来代表程序中“3.1415926”的这个字符串,在编译预处理时,将程序中在该命令以后出现的所有的PI都用“3.1415926”代替,不仅使用简单可避免错误,而且还可以做到一改全改的效果。
简单的变量式宏定义,在上一小节通过几个范例较为详细地说明了其适用的各种情况。和变量定义一样,宏也是可以嵌套定义的。例如:
#define A (1+2)
#define B A*A
#define C B+B
先定义了A,然后定义B,最后又定义了C,环环相套,次序不能颠倒。宏展开后如下:
#define A (1+2)
#define B (1+2)*(1+2)
#define C (1+2)*(1+2)+ (1+2)*(1+2)
文件中的宏定义并非是从定义就有效到文件结束,可以圈定宏定义的作用范围,使用的方法是:
#define /*宏定义开始*/
…… /*宏定义范围*/
#undef /*宏定义结束*/
其定义的一般形式为:
#define 标识符(参数列表) 字符串
字符串中包含标识符中的参数,参数列表可由一个或者多个参数组成。因为外观像调用含义一样,所以称之为函数式宏定义,也可以称为带参数的宏定义。
通常宏定义必须是单行的,但是也可以使用多行来定义一个宏,定义的方法就是使用反斜杠“\”。比如:
#define min(x,y)\ /*多行宏定义开始*/
((x)<(y)?(x):(y)) /*多行宏定义结束*/
这个指令写在两行中,第2行为了美观,插入了一些空白符,使用的是空格。但是需要注意的是:第1行的反斜杠“\”必须放在该行的结尾,也就是输入了“\”后,紧接着的就是回车。
文件包含指令的功能是把指定的文件插入该指令行位置取代该指令行,从而把指定的文件和当前的源程序文件连成一个源文件。在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头用包含指令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用部分,从而节省时间,并减少出错。
通常以头文件来编写这些被包含的文件,也就是以.h作为这些文件的扩展名,当然不是非要这样命名才可以。也可以使用.c为扩展名,或者干脆就不写扩展名,这样做都是允许的,只是使用.h更普遍,因为它能够表示该文件的性质。
文件包含#include指令的作用,就是把文件按照顺序合并成一个文件,这是预处理阶段。在编译时,对已经经过预处理的文件myfile.c作为一个源文件单位进行编译。
也可以看出了文件包含的意义,既提高了公用文件的使用率,节省了开发的时间,也减小了程序出错误的几率。
条件编译形式
调试中使用条件编译
文件嵌套包含和条件编译
assert()宏
一般的条件编译形式如下。
1. #ifdef 形式
作用是标识符已经被定义,则对程序段1进行编译,否则编译程序段2。
2. #ifndef形式
这种形式的条件编译与第1种形式相同,内容相反。只是ifdef替换成了ifndef,即如果标识符没有被定义过,就会编译程序段1,否则就编译程序段2。
每个大型程序都要经历内部测试和外部测试,最终发布版,即使是发布后,应用程序还会存在bug,需要修改完善。程序开发不是一蹴而就的,需要反复地修改完善。修改代码,就避免不了使用调试功能,输出丰富的调试信息有助于尽快地完成程序的修复。
可以在程序中输出参数的值,判断什么位置出现了问题,但是如果程序调试结束再一一删除输出值的语句,工作量会比较大。因此特别是对于大型程序,就可以在调试中使用宏。
由于嵌套包含文件的原因,一个头文件可能会被多次包含在一个源文件中,而使用条件指示符就可以防止这种头文件的重复处理。
只要不存在两个必须包含的头文件,要检查一个同名的预处理器常量这样的情形,这个策略就能够很好地运作。#ifndef指示符常被用来判断一个预处理器常量是否已被定义,以便有条件地包含程序代码。#ifndef 除了用于防止重复包含,还可以用于针对不同环境的条件编译。
assert()宏是在标准函数库<assert.h>头文件中定义的,称为断言。assert()宏可以让你在程序中插入任何表达式,用来诊断表达式的值是否为false,也就是为0。作为assert()宏的参数的表达式的结果是一个整型数据。
例如:
assert(a==b);
当a和b相等时,表达的结果是1,也就等同于true;如果a和b不等,结果就是0,也就是false,然后可根据结果决定是否终止程序。当程序出现异常时,可以使用abort()函数以非正常方式立即结束应用程序。