1 / 36

第六章 编译预处理

第六章 编译预处理. 本章学习重点: 掌握不带参数的宏定义的格式及使用方法; 掌握带参数的宏定义的格式及使用方法; 掌握文件包含的格式及使用方法; 掌握条件编译的格式及使用方法。. 第十四讲宏定义、文件包含 和条件编译. 一、不带参数的宏定义 二、带参数的宏定义 三、文件包含处理 四、条件编译 练一练 本章小结. 结束. 一、不带参数的宏定义. 【思考题 6-1 】 输入圆的半径,求圆的周长、面积(要求使用无参宏定义圆周率)。 ( 一 )程序分析 在程序中要用到无参宏定义,要注意例程开头的宏定义语句的书写格式,掌握程序在编译之前怎样替换该宏?.

iorwen
Download Presentation

第六章 编译预处理

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第六章 编译预处理 本章学习重点: 掌握不带参数的宏定义的格式及使用方法; 掌握带参数的宏定义的格式及使用方法; 掌握文件包含的格式及使用方法; 掌握条件编译的格式及使用方法。

  2. 第十四讲宏定义、文件包含和条件编译 一、不带参数的宏定义 二、带参数的宏定义 三、文件包含处理 四、条件编译 练一练 本章小结 结束

  3. 一、不带参数的宏定义 【思考题6-1】输入圆的半径,求圆的周长、面积(要求使用无参宏定义圆周率)。 (一)程序分析 在程序中要用到无参宏定义,要注意例程开头的宏定义语句的书写格式,掌握程序在编译之前怎样替换该宏? 返回到本章目录

  4. (二)编写程序代码如下: #define PI 3.1415926 /*PI是宏名,3.1415926为用来替换宏名的常数*/ main() { float radius,length,area; printf("Input a radius: "); scanf("%f",&radius); length=2*PI*radius; /*引用无参宏求周长*/ area=PI*radius*radius; /*引用无参宏求面积*/ printf("length=%.2f,area=%.2f\n",length,area); } 返回到本章目录

  5. (三)调试程序及运行结果 程序中输入圆的半径值2.4,程序的运行结果如下: 返回到本章目录

  6. 1.不带参数的宏定义 (1)不带参数宏定义的一般形式如下: 其中,#define是宏定义的命令,标识符和字符串之间用空格分开。标识符称为“宏名”,通常使用大写英文字母和有直观意义的标识符命名,以区别于源程序中的其他标识符。字符串又称为宏体,一般是常数、关键字、语句、表达式,也可以是空白。 #define 宏名 字符串 返回到本章目录

  7. (2)功能。不带参数的宏定义的功能是将宏体字符串符号化。在程序中凡出现该宏名的位置,经编译预处理的加工,都被替换成对应的宏体字符串,称之为“宏展开”。(2)功能。不带参数的宏定义的功能是将宏体字符串符号化。在程序中凡出现该宏名的位置,经编译预处理的加工,都被替换成对应的宏体字符串,称之为“宏展开”。 由于宏定义不是C语句,不必也不能在行末加分号。 返回到本章目录

  8. 2.关于宏的几点说明 (1)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量,增加程序的可读性,而且不易出错。 (2)编译预处理时,将程序中PI用3.1415926代替,与宏调用的过程相反,这种将宏名替换成字符串的过程称为“宏展开”。 (3)C语言中,用宏名替换一个字符串是简单的转换过程,不作语法检查。若将宏体的字符串中符号写错了,宏展开时照样代入,只有在编译宏展开后的源程序时才会提示语法错误。 返回到本章目录

  9. 2.关于宏的几点说明 (4)一个宏名只能被定义一次,否则会出现重复定义的错误。 (5)宏定义可以嵌套。在宏体中,可以出现已定义的宏名,例如: #define PI 3.1415926 #define PIR (PI*r) /*PI为已定义的宏名*/ (6)如果宏定义一行书写不下,可用反斜线“\”和回车键来结束本行,然后在下一行继续书写。 返回到本章目录

  10. 2.关于宏的几点说明 (7)程序中出现的由双引号括起来的字符串,即使和宏名相同,也不进行宏替换。例如,在输出函数printf()中如果在双引号内有与宏名相同的字符串,也不认为是宏,只认为是普通字符串原样输出。 (8)宏定义命令行放在源程序的函数外时,宏名的作用域从宏定义命令行开始到本源文件结束。 (9)宏名的作用域可以使用#undef命令终止,形式如下: #undef 标识符 返回到本章目录

  11. 二、带参数的宏定义 【思考题6-2】求两个数a和b中的最大值,要求用带参数的宏定义来实现。 (一)程序分析 这个程序可以使用函数来实现,但这里我们要求用带参数的宏定义来实现该功能。要注意带参数的宏定义的格式及在编译前进行替换的方式。 返回到本章目录

  12. (二)编写程序代码 #define MAX(a,b) ((a)>(b)?(a):(b)) /*MAX为带参数的宏定义,a、b为其参数*/ main() { int a,b,max; printf("\nPlease input 2 integer number:"); scanf("%d%d",&a,&b); max=MAX(a,b); /*求a和b中的最大数,MAX为带参数的宏定义*/ printf("the max value is :%d",max); } 返回到本章目录

  13. (三)调试程序及运行结果 程序输入两个整数3和5,程序运行结果如下: 返回到本章目录

  14. 3.带参数的宏定义 1)带参数宏定义的一般格式 2)带参数的宏的调用和展开 (1)带参数的宏的调用格式如下 (2)带参数的宏展开。带参数的宏展开是用宏调用提供的实参字符串,直接置换宏定义命令行中相应形参字符串,非形参字符保持不变。 #define宏名(形式参数表) 字符串 宏名(实参表) 返回到本章目录

  15. 4.关于带参数的宏定义的几点说明(1)定义有参数的宏时,宏名应当与参数表的左括号紧紧相连。否则,C编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。4.关于带参数的宏定义的几点说明(1)定义有参数的宏时,宏名应当与参数表的左括号紧紧相连。否则,C编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。 (2)宏定义时,应将整个字符串以及其中的各个参数均用圆括号括起来,以确保宏展开后字符串中各个参数的计算顺序的正确性,避免出现错误。 (3)在宏定义中的形参是标识符,而宏展开的实参可以是表达式。 (4)虽然有参数的宏与有参数的函数确实有相似之处,但不同之处更多,主要有以下几个方面。 返回到本章目录

  16. 调用有参函数时,是先求出实参的值,然后再复制一份给形参。而展开有参宏时,只是将实参简单地置换形参。 ② 在有参函数中,形参是有类型的,所以要求实参的类型与其一致;而在有参宏中,形参是没有类型信息的,因此用于置换的实参,什么类型都可以。有时,可利用有参宏的这一特性,实现通用函数的功能。 ③ 函数调用是在程序运行中处理的,临时给它分配存储单元。而宏代换是在编译时进行的,并不给它分配存储单元,不进行数值的传递,也没有返回值。 ④ 函数的形参和实参都要求定义类型,而且二者要求一致,若二者不一致时,还要进行类型转换。而对于宏则不存在此类问题,宏没有类型,参数也没有类型,它们都仅是一种符号代表,宏展开时只是进行对应符号的代换。 ⑤ 在程序中使用宏时,宏展开后源程序会变长。而函数调用不会使源程序变长。 ⑥ 宏代换不占程序运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。 返回到本章目录

  17. 三、文件包含处理 【思考题6-3】修改【思考题6-2】,要求将【思考题6-2】中的带参数宏定义语句单独放在一个头文件中,主函数单独放在一个源程序文件中,功能同【思考题6-2】。 (一)程序分析 将一部分内容放到一个头文件中,在编译预处理时,将头文件内容先替换到文件包含语句部分,然后再进行编译。注意头文件和源程序的组成。 返回到本章目录

  18. (二)编写程序代码 (1)in6_3.h的内容: #define MAX(a,b) ((a)>(b)?(a):(b)) /*MAX为带参数的宏定义,a、b为其参数*/ (2)主程序内容: #include "in6_3.h" /*文件包含命令,将in6_3.h头文件包含到该程序中*/ main() { int a,b,max; printf("\nPlease input 2 integer number:"); scanf("%d%d",&a,&b); max=MAX(a,b); /*求a和b中的最大数,MAX为带参数的宏定义*/ printf("the max value is :%d",max); } (三)调试程序及运行结果 程序运行结果同【思考题6-2】的例程结果。 返回到本章目录

  19. 5.文件包含处理 (1)编译预处理的文件包含功能。 ① 编译预处理的文件包含功能是一个源程序通过#include命令把另外一个源文件的全部内容嵌入到源程序中来。 ② 编译预处理程序把#include命令行中所指定的源文件的全部内容放到源程序的#include命令行所在的位置。 ③ 在编译时并不是作为两个文件连接,而是作为一个源程序编译,得到一个目标文件。 返回到本章目录

  20. (2)文件包含#include命令格式主要有以下两种。(2)文件包含#include命令格式主要有以下两种。 ① 只检索C语言编译系统所确定的标准目录,格式如下: ② 首先对使用包含文件的源文件所在的目录进行检索,若没有找到指定的文件,再在标准目录中检索,格式如下: (3)#include命令的嵌套使用。当一个程序中使用#include命令嵌入一个指定的包含文件时,被嵌入的文件中还可以使用#include命令,又可以包含另外一个指定的包含文件。 #include <文件名> #include "文件名" 返回到本章目录

  21. 四、条件编译 【思考题6-4】输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出。 (一)程序分析 定义一个常量LETTER,通过判断LETTER的值来对某些程序进行条件编译。当LETTER值为1时将字符串str中的全部字符转换成大写字母,否则将大写字母转换成小些字母。注意条件编译的语句格式。 返回到本章目录

  22. (二)编写程序代码 #include "string.h" #define LETTER 1 main() { char str[100], c; int i; i=0; printf("\nPlease input a string:\n"); gets(str); /*注意:如果要输入带空格的字符串必须用gets函数*/ 返回到本章目录

  23. (三)调试程序及运行结果 输入一个字符串"Hello,all the world people!",程序将这一串小写字符变成大写字符"HELLO,ALL THE WORLD PEOPLE!"。程序运行结果如下: 返回到本章目录

  24. while((c=str[i])!='\0') { i++; #ifdef LETTER if(c>='a' && c<='z') c=c-32; #else if(c>='A' && c<='Z') c=c+32; #endif printf("%c", c); } } 返回到本章目录

  25. 6.条件编译 所谓条件编译,是指对源程序进行选择性编译。 1)#ifdef命令(或#ifndef命令) (1)#ifdef命令的一般格式如下 (2)功能。#ifdef命令的功能是如果“标识符”已经被#define命令定义过,则编译程序段1,否则编译程序段2。 #ifdef 标识符 程序段1 [#else 程序段2] #endif 返回到本章目录

  26. 在不同的系统中,一个int 型数据占用的内存字节数可能是不同的。 利用条件编译,还可使同一源程序既适合于调试(进行程序跟踪、打印较多的状态或错误信息),又适合高效执行要求。 #ifndef命令格式与#ifdef命令一样,功能正好与之相反。 返回到本章目录

  27. 2)#if命令 (2)#if命令一般格式如下: (2)功能。#if命令的功能是当表达式为非0(“逻辑真”)时,编译程序段1,否则编译程序段2。 #if 常量表达式 程序段1 [#else 程序段2] #endif 返回到本章目录

  28. (3)条件编译和if语句的区别。 ① if语句控制某些语句是否被执行,#if命令控制着某个程序段是否被编译。 ② 用if语句调试程序成功后,其调试语句仍被编译成目标代码,只是不再执行,成为废码。而使用条件编译调试程序成功后,其调试语句不再被编译,不生成目标代码,没有废码产生,空间借用率较高。 返回到本章目录

  29. 练一练 【练习6-1】设计一个程序,从3个数中找最大数,用带参数的宏定义实现。 解:因为条件语句可以通过一条语句实现求3个数中最大值的功能,所以可以设一个带3个参数的宏定义,宏的值用一个条件语句来实现。 注意:在宏体部分的每一个参数和整个宏体都要用圆括号括起来,以防止宏展开时出错。 返回到本章目录

  30. 源程序如下: #define MAX(a,b,c) ((a)>(b)?((a)>(c)?(a):(c)):((b)>(c))?(b):(c)) main() { int a,b,c,max; printf("\nPlease input 3 int number:"); scanf("%d%d%d",&a,&b,&c); max= MAX(a,b,c); printf("\nThe max number is:%d",max); } 程序运行结果如下: 返回到本章目录

  31. 【练习6-2】用条件编译方法实现以下功能:输入一行电报文字,任选两种输出方式,一为原文输出,二为将字母变成其下一个字母(如'a'变成'b','b'变成'c',依次类推,最后'z'变成'a',其他字符不变)。用#define命令来控制是否要译成密码,例如:【练习6-2】用条件编译方法实现以下功能:输入一行电报文字,任选两种输出方式,一为原文输出,二为将字母变成其下一个字母(如'a'变成'b','b'变成'c',依次类推,最后'z'变成'a',其他字符不变)。用#define命令来控制是否要译成密码,例如: #define CHANGE 1 则输出密码。 若: #define CHANGE 0 则不译成密码,按原文输出。 返回到本章目录

  32. 解:当字母不为'z'或'Z'时,将字母变成其下一个字母,就是将字母的ASCII码值加1即可实现;当字母为'z'或'Z'时,令该字母等于'a'或'A'。条件编译可以用#ifdef命令来实现。解:当字母不为'z'或'Z'时,将字母变成其下一个字母,就是将字母的ASCII码值加1即可实现;当字母为'z'或'Z'时,令该字母等于'a'或'A'。条件编译可以用#ifdef命令来实现。 源程序如下: #include "string.h" #define CHANGE 1 /*可将1改为0,则进行字母替换*/ main() { char str[100],ch; int i,len; printf("\nPlease input a string:"); gets(str); len=strlen(str); 返回到本章目录

  33. #if CHANGE for(i=0;i<len;i++) if(str[i]>= 'a'&&str[i]<'z'|| str[i]>= 'A'&&str[i]<'Z') str[i]=str[i]+1; else { if(str[i]== 'z') str[i]= 'a'; else if(str[i]== 'Z') str[i]= 'Z'; } #endif puts(str); } 返回到本章目录

  34. 程序运行结果如下: 返回到本章目录

  35. 本章小结 • C程序的编译可分为编译预处理和正式编译两个步骤。编译预处理是在将源程序生成目标文件之前,对源程序的加工。正确使用编译预处理命令可以有效提高程序的开发效率。 • C语言提供了多种预处理命令,常用的有宏定义、文件包含和条件编译。 • 宏定义命令为#define,是用一个标识符来代表一个字符串,这个字符串可以是常量、变量或表达式。在宏展开中将用该字符串代替宏名。宏定义还可以带参数,宏展开时除用字符串代替宏名外,还应用实参代替形参。 返回到本章目录

  36. 本章小结 • 文件包含命令为#include,它可以把一个或多个文件嵌入到另一个源文件中进行编译,结果将生成一个目标文件。 • 条件编译根据条件成立与否,选择编译程序中满足条件的程序段,便于程序调试,并使生成的目标程序较短,减少内存开销,提高程序运行效率。 返回到本章目录

More Related