在编译过程开始之前,C语言预处理器首先对程序代码做了必要的转换处理。我们称这种转换处理为预处理。
C语言中,以“#”开头的语句统称编译预处理命令。这些命令必须在一行的开头以“#”开始,末尾不加分号,并且每条命令独占一行,以区别于一般的C语句,它们可以放在程序的任何位置。
一 宏
宏是一种定义,它就是给一个语句块(宏体)定义了一个名字。#define 宏名 宏体
宏的好处:
1.提高了可读性:数字本身并没有什么实际的意义。例如:#define PI 3.1415926
2.减少了书写错误:我们用#define 定义个常量PI代表3.1415926, 用宏定义后,只要一处校验,其它相同的错误也解决了。
3.维护性:有时我们需要将某个特定数据,在程序中出现的所有实例加以修改,我们只做一个改动就达到目的。#define MAXSIZE 100
4.提高运行速度:在函数调用的时候会带来重大的系统开销,因此我们有时希望有一个程序块,看上去像一个函数,但却没有函数调用的开销#define max(a,b) (((a)>(b))?(a):(b))
预处理时执行“替换”动作:把源程序中使用宏名的地方替换成宏体。
宏分为两种:1.无参宏2.带参宏 区别宏名字后有“参数”就是带参宏,也叫宏函数。
带参数的宏(带参宏)一般形式 #define 宏名(参数表) 宏体 宏展开:“形参”用“实参”替换,其它字符保留。
宏定义的特点:
1. 宏定义不是C语句;
2. 宏定义不是typedef;
3. 宏定义不是函数。宏定义就是使用宏名代替一个字符串,在预编译时替换 (注:对程序中用双引号括起来的字符串内部的字符,即使与宏名相同,也不进行替换。)
函数调用和宏定义的区别:
函数调用
程序在处理时,先求出实参表达式的值,然后代入形参。
编译时会对实参进行类型检查
函数调用不会使源程序变长,
函数调用占运行时间(建栈、退栈)。
宏定义
带参宏只是进行简单的字符替换。
带参宏就是字符串替换
宏展开会使源程序变长。
宏替换不占运行时间,只占预编辑时间。
二条件编译
一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。
方式1:
#ifdef 标识符
程序段1
#else
程序段2
#endif
作用是:当标识符已经被定义过,则编译程序段1,否则编译程序段2,#else部分可以省略。
方式2:
#ifndef 标识符
程序段1
#else
程序段2
#endif
作用是:当标识符没有被定义过,则编译程序段1,否则编译程序段2,#else部分可以省略。
方式3:
#if 表达式
程序段1
#else
程序段2
#endif
作用是:当表达式的值为真,则编译程序段1,否则编译程序段2。
条件编译方式1举例:在工程的DEBUG版本中打印调试信息
/*********test.c***************/
#ifdefDEBUG
/*做些打印工作如printf....,这部分工作在调试版本中进行,发布版不需要*/
#else//else分支可省略,一般都省略
#endif
/*********test.c***************/
使用时如果想打印调试信息,则加上#define DEBUG 就可以完成打印调试信息的功能
条件编译方式2举例:用来防止一个源文件对同一个头文件的多次重复包含(思考什么是包含头文件?)
/*********config.h***************/
#ifndef_CONFIG_H_
#define _CONFIG_H_
#include<stdio.h>
#include<stdlib.h>
//......等等需要的头文件
#else//else分支这部分通常省略
#endif
/*********config.h***************/
条件编译方式3举例:只使有效的代码参加编译,提高效率
//某config.h内容如下:
#define Fosc16000000
#define Fcclk(Fosc* 4)
#define Fpclk(Fcclk/ 4) * 1
// 可能是1、2、4
//在编译前就能决定下来到底是几
//在target.c中需要根据Fpclk、Fcclk的值设置VPBDIV
// 于是推荐做法如下:
#if (Fpclk/ (Fcclk/ 4)) == 2VPBDIV = 2;
#elseVPBDIV = 1;#endif
不要用#if…/#endif来代替注释
1. 单行注释可以写成//……或/*………………*/,而#if…#endif不可。(一行只能一条预编译指令)2. 注释写了是为了给人们看的,是解释说明。而/#if……/#endif是给预编译器看的,对象不同。
三 文件包含
文件包含命令会导致重复定义。头文件中不要定义变量,防止头文件重复包含的条件开关只能使得一个源文件不会对该头文件多次包含
四 头文件
头文件应含有的内容:
1.函数原型;
2.定义的宏常量和数据类型等各文件都要使用的东西。这样既避免了各文件重复作相同的事情,又可以取得“一改全改”的统一。
头文件含有变量定义危险!若对加static修饰,则各个包含该头文件的文件各自拥有自己的一份拷贝
头文件结构头文件由三部分内容组成:(1)头文件的版权和版本声明(参见示例1-1)。(2)预处理块。(3)函数声明和数据类型定义等。