1.#define
这个编译预处理指令啊,其实我们很早就见过了,我们的第一个c语言的程序,那个hello world,它的第一行,他的第一个字符那个井号,就说明它是一条编译预处理指令所有的c语言的编译预处理指令呢
都是以井号开头的,实际上这些井号开头的东西,,它并不是c语言的成分,但是呢任何一个c语言的程序都离不开他们,之所以说它不是c语言的成分,是因为这些东西,你看比如说include,它不是c语言的关键字啊,也不是只有c语言在使用,这些编译预处理指令,其他语言也可以去使用,那么我们之前看到的都是井号include啊,它是这个用来呃引入一个头文件的啊,这是我们到后面讲大程序结构的时候
我们再来说,我们先来看另外一个小东西.叫做#define.呃,这事我们之前没做过啊,我们来做一下.
原本呢我们可能写程序是这样写啊.我们我们要用到一个叫做pi的东西,然后呢我们之前提到过说,那,你如果呃程序里面直接写,比如说我这句printf直接写成说2乘以这个3.14159,那这个3.14159是个什么样的东西,别人可能会有各种各样的想法.所以呢我们更好的方式是把这个常数3.14159呢做成一个constant double啊,做成一个不可变的变量,这是c99 的做法如果你在用ansi c,你在用更老的版本,它没有const这样的东西可以用,那个时候人们就要用这样的方法说,我们要define一个叫做pi的东西,它是3.14159.如果这样子,当然这个程序还是有一定.所以这一行define我们就定义了一个符号,我们把这样定义出来的符号叫做宏.pi是这个宏的名字,后面的数字是它的值.
c语言的程序在编译的时候呢,准确地说在编译之前,因为它叫做编译预处理指令对吧,会做一次编译预处理.在那次预处理里头呢,会把所有你的程序里头的这种pi都替换成那个数字.呃,我们有一个办法,可以去看到在编译过程中留下来的那些临时文件,如果是gcc的话,是需要这么做的.
除了我们最终要拿来运行的那个a.out之外,我们源代码.c,我们还多了一个.i,.o和.s,当然这三个文件它这个排列是按照字母顺序啊,他并不是真正程序编译过程当中产生的那个顺序.首先产生的是点i,然后从.i得到了.s,再从.s呢得到了那个.o,再从.o呢得到了那个a.out.源代码(.c)经过编译预处理做完以后呢产生了临时的一个中间结果的文件,就是把当中所有的编译预处理指令都执行完,比如说define那个宏要拿去做替换的,把它替换掉.然后对.%s才是.c实际编译产生的文件,这是汇编代码文件,然后汇编代码文件去做汇编,变成了一个目标代码文件,这个目标代码文件再经过链接,最后就形成了一个可执行的东西.
所以#define这个东西呢是一个编译预处理指令,它会在编译器开始去编译之前,先把你的源代码做一遍编译预处理,把里面的所有的宏都替换掉.把出现了你定义的那些宏的名字的地方都替换成那个后面的值,后面的那个值可以很复杂,可以带空格,可以带各种标点符号,没关系.反正第一个是单词,后面其他的都是值,然后原封不动地做文本的替换.特别要小心的是,在那个define后面不能加分号,因为这不是一句c的语句,c的语句才用加分号.
2.宏
如:
3.没有值的宏
其实我们还会在程序中看到,我们会定义没有值的宏.如#define _debug.像这种情况呢,我们是告诉编译器说我们有了这个宏了,可是我们不需要它有什么样值,因为我们在后面可能会去检查说这个宏存在还是不存在,它如果存在,我们编译这部分代码,如果不存在,我们可能编译另一部分代码.这是用来做条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了.
4.预定义的宏
另外呢,我们的编译器有一些预先定义的宏,呃,它们都是带着翅膀的,就是前面有两个下划线,后面也有两个下划线,好像带着翅膀一样.那么这些宏呢,一般是用来表达一些特殊的东西,可以让编译器替你插入一些特殊的值.
比如说第一个是表达说这个源代码文件的行号,当前所在的那个行的行号,第二个表达的是源代码文件的文件名,第三个和第四个分别表示的是源文件编译的日期和时间.我们可以来试一下这些有意思的预定义宏:
运行结果:
最后一个用来表示编译器是否遵循ansi c标准,然而现在已经基本没用了.