《C课件8.预处理》由会员分享,可在线阅读,更多相关《C课件8.预处理(41页珍藏版)》请在金锄头文库上搜索。
1、高质量C语言编程 预处理,第八章:,目标: 1)掌握预处理的概念 2)领会文件包含的意义 3)了解头文件的结构和内容 4)理解无参宏和带参宏 5)学会使用条件编译,时间:2学时 教学方法:PPT+练习,预处理的概念,在编译过程开始之前,C语言预处理器首先对程序代码做的必要的转换处理,这种转换处理为预处理。,C语言中,以“”开头的语句统称编译预处理命令。 这些命令必须在一行的开头以“”开始,末尾不加分号,并且每条命令独占一行,以区别于一般的C语句,它们可以放在程序的任何位置。 不正确的预处理命令: #include /同时包含两个头文件 #define M 5 #define N 10 /同时定
2、义两个宏,什么是宏,宏是一种定义,就是给一个语句块(宏体)定义了一个名字。 #define 宏名 宏体,使用宏的好处?,1.提高了可读性:因为数字本身没有表示出任何意义。 例如:#define PI 3.1415926 2.减少了书写错误:用#define 定义PI用来代表常量3.1415926,定义了宏之后, 只要一处校验,其它相同的错误也解决了。 3.可维护性:当需要将程序中的某个特定数据在所有出现处修改,只一条语句就能达到目的:#define MAXSIZE 100 4.提高运行速度:函数在调用时会带来重大的系统开销,因此希望一个程序块,看上去像一个函数,但却没有函数调用的开销,可以写成
3、宏:#define max(a,b) (a)(b)?(a):(b),宏定义的好处,比较下面两段代码: for( int i = 0 ;i100; +i ) / #define MIN 0 #define MAX 100 for( int i = MIN ;i MAX; +i ) / ,宏提高了代码的可读性,只表示循环是从0到100,至于含义是什么,没有表示,由读者去猜!,明确表示循环是从最小值到最大值之间,含义不言自明! 可见带给读者的信息量是不同的。,宏替换,#define PI 3.1415 void set(void); void main(void) float f1= PI; #un
4、def PI void set(void) float f2=PI; ,预处理时执行“替换” 动作:把源程序中使用宏名的地方替换成宏体。 这个过程叫“宏展开”或“宏替换”。,无参宏和带参宏,宏分为两种:1.无参宏 2.带参宏,无参宏的例子: #include void main(void) printf(“%dn“,NUM); ,#define N 2 #define M N+1 #define NUM (M+3)*M/2,12,无参宏和带参宏,一般形式: #define 宏名(参数表) 宏体 宏展开:“形参”用“实参”替换,其它字符保留。,带参数的宏,不能用空格隔开,#define f (x
5、) (x)-1),f 和后面的(x)之间多了空格 如果希望定义 f(x) 为 (x)-1),必须写成: #define f(x) (x)-1),宏调用时, f(3) 和 f (3)分别是多少?,都可以. 2,无参宏和带参宏,带参宏的例子: #include #define s(a,b) a*b void main(void) printf(“%dn“,s(1,2); ,#undef s printf(“%dn“,s(1,2);,2,“宏取消”时仅用宏名即可,不要再带参数(会警告),宏定义的特点,1. 宏定义不是C语句; 2. 宏定义不是typedef; 3. 宏定义不是函数。 宏定义就是使用宏
6、名代替一个字符串,在预编译时替换。,对程序中用双引号括起来的字符串内部的字符,即使与宏名相同,也不进行替换。,#include #define R Hi int main(void) printf(“R”); return 1; / 此处的R是否是宏?,宏定义的特点,宏不是类型定义。,#define STU struct student* STU a,b; typedef struct student* STU; STU x,y; 问:a,b,x,y 都是指针吗?,宏和typedef的区别:,宏定义的特点,#define max(a,b) (a)(b)?(a):(b) void main(vo
7、id) int x=2,3,1; int biggest = x0; int i = 1; biggest =max (biggest, xi+); printf(“%dn“,biggest); ,宏替换时,赋值语句扩展为: biggest = (biggest)(xi+)?(biggest): (xi+);,宏并不是函数。下例可以体会到它们的区别:,宏定义的特点,函数调用和宏定义的区别,宏定义的特点,#define PF(x) (x)*(x) void main(void) int a = 2, b = 3, c; c = PF(a+b) / PF(a+1); printf(“nc=%d “
8、,c); ,比较以下三个宏的优劣:,#define PF(x) x*x #define PF(x) (x)*(x),宏定义的特点,按第一种宏定义: c = a+b*a+b/a+1*a+1; 按第二种宏定义: c = (a+b)*(a+b)/(a+1)*(a+1); 按第三种宏定义: c = (a+b)*(a+b)/(a+1)*(a+1);,1. 对于不带参的宏,若宏值多于一项,一定要使用括号: #define MAX (M+N) 2. 对于带参的宏,要给每个参数加上括号,否则可能影响计算的优先级,宏定义的特点,#include #define judge(e) if(e) printf(“ER
9、ROR!n“) void main(void) int x = 1, y = 0; if(x0 ,思考:这样定义宏有什么问题吗?,宏展开后变成这样: void main(void) int x = 1, y = 0; if(x0 什么也没干!,练习:,下面的宏定义有何隐患? #define abs(x) (x=0)?x:-x c = abs(a+b);,修改: #define abs(x) (x)=0?(x): -(x),宏展开后相当于: c= (a+b=0)?a+b:-a+b;,条件编译,在没有专门设定的情况下,源程序中所有的行都参加编译。 但是有时希望其中一部分源代码只在满足一定条件时才进
10、行编译,也就是为一部分源代码指定编译的条件,这就是“条件编译”。 预编译器对参加编译的源程序保留下来,不参加编译的部分用空白字符替换。,条件编译,方式2: #ifndef 标识符 程序段1 #else 程序段2 #endif,方式3: #if 表达式 程序段1 #else 程序段2 #endif,方式1: #ifdef 标识符 程序段1 #else 程序段2 #endif,作用是:当标识符已经被定义过,则编译程序段1,否则编译程序段2,#else部分可以省略。,作用是:当标识符没有被定义过,则编译程序段1,否则编译程序段2,#else部分可以省略。,作用是:当表达式的值为真,则编译程序段1,否
11、则编译程序段2。,条件编译,条件编译方式1举例:在工程的DEBUG版本中打印调试信息 /*test.c*/ #ifdef DEBUG /* 做些打印工作如printf,这部分工作在调试版本中进行,发布版不需要*/ #else /else分支可省略,一般都省略 #endif /* test.c */ 使用时如果想打印调试信息,则加上#define DEBUG 就可以完成打印调试信息的功能,条件编译,条件编译方式3举例:只使有效的代码参加编译,提高效率 /某config.h内容如下: #define Fosc 16000000 #define Fcclk (Fosc * 4) #define Fp
12、clk (Fcclk / 4) * 1 / 可能是1、2、4 /在编译前就能决定下来到底是几 /在target.c中需要根据Fpclk 、Fcclk 的值设置 VPBDIV / 于是推荐做法如下: #if (Fpclk / (Fcclk / 4) = 2 VPBDIV = 2; #else VPBDIV = 1; #endif,条件编译,不要用#if/#endif来代替注释,1. 单行注释可以写成/或/*/,而#if#endif不可。(一行只能一条预编译指令) 2. 注释写了是为了给人们看的,是解释说明。而/#if/#endif是给预编译器看的,对象不同。,1. 只使有效地代码参加编译。 2.
13、 区分程序的DEBUG版和RELEASE版。 Debug版本:#define _DEBUG Release版本:#define NDEBUG 3. 防止头文件被重复编译。,使用条件编译的好处?,文件包含,文件包含:就是在预编译时将头文件的内容加入到包含文件中。,思考: #include“包含”进来了哪些内容? 用到哪个函数包含哪个函数? 有没有函数的实现?,文件包含,#include命令的一般形式:(区别?) #include “文件名” #include #include “绝对路径” 文件包含可以指定所包含文件的绝对路径: #include “C:Program FilesMicrosof
14、t Visual StudioMyProjects01.h“ 文件包含可以指定所包含文件的相对路径: #include “MyProjects01.h “ 推荐使用相对路径,文件包含,#include “02.h“ #include “01.h“ #include int main(void) printf(“程序开始!n“); PrintStar(m) ; return 0; ,#include “01.h“ void PrintStar(int n);,#include int m=15;,02.h,01.h,file1.c,#include “02.h“ void PrintStar(i
15、nt n) int i = 0; for(i = 0; in; +i) printf(“*“); printf(“n“); ,file2.c,文件包含,文件包含命令会导致重复定义,例如:文件file1.c包含了文件01.h和文件02.h,文件02.h也包含了文件01.h。,01.h,02.h #include “01.h”,file1.c #include “02.h” #include “01.h”,int m = 15;,int m =15;,int m = 15;,int m = 15;,文件包含,解决方法:,防止头文件包含导致的重复定义。,#ifndef 01_H #define 01_H #endif,01.h,文件包含,#include #include #define N 3 #define S(a,b) (a)*(b) #define NEUSOFT int main() int i = N; printf(“N = %dn“,i); #ifdef NEUSOFT printf(“NEUSOFT DEFINED!n“); #else printf(“NEUSOFT UNDEFINED!n“);