C语言教程,编译预处理
学习目的:预处理命令的作用是改进
程序设计环境,提高编程效率。通
过宏定义、文件包含和条件编译的
讲解,使学生熟悉并能用这三种预处理命令编写简单程序。
编译预处理内容介绍
1 预处理概述
2 宏定义
3 文件包含
4 条件编译
预处理命令概述
预处理的概念:在对 C源程序进行编译之前所
进行的处理。
TC中按下 Ctrl+F9所完成的任务:预处理 ->编译
->连接 ->运行。
C中提供的预处理功能:宏定义、文件包含、
条件编译。
C中的预处理命令,#开头、无分号结尾、一行
一个、名字可以作为一般标识符,如,
int define=3;
宏定义
,宏”的概念:用一个标识符来表示一个
字符串,该标识符称为“宏名”
对“宏”的处理:在编译预处理时,对
程序中所有出现的“宏名”,都用宏定
义中的字符串去代换,这称为“宏代换”
或“宏展开”
,宏”分为有参数和无参数两种
“宏定义”在程序的开头,函数的外部
无参宏定义 的一般形式为,#define 标识符 字符串
“字符串”可以是常数、表达式、格式串等,如,
#define PI 3.1415926
#define NL,\n”
#define D,%d”
符号常量的定义就是一种无参宏定义
对程序中反复使用的表达式可以进行宏定义
例如,#define M (y*y+3*y)
【 例 】 #define M (y*y+3*y)
main( )
{ int s,y;
printf("input a number,");
scanf("%d",&y);
s=3*M+4*M+5*M;
printf("s=%d\n",s);
}
2.特别注意:在宏定义
中表达式 (y*y+3*y)两边的
括号不能少。否则会发生
错误。如当作以下定义后,
#difine M y*y+3*y
3.在宏展开时将得到下
述语句,
s=3*y*y+3*y+4*y*y+3*
y+5*y*y+3*y;
1.s=3*M+4*M+5*M在预处
理时经宏展开后该语句变为,
s=3*(y*y+3*y)+4*(y*y+3*y)
+5*(y*y+3*y);
对于宏定义说明,
1)在宏展开只是一种简单的代换,预处理程序对
它不作任何检查。如有错误,只能在编译已被
宏展开后的源程序时发现。
2)宏定义不是说明或语句,在行末不必加分号,
如加上分号则连分号也一起置换。
3)宏定义必须写在函数之外,其作用域为宏定义
命令起到源程序结束。如要终止其作用域可使
用 # undef命令。
例如,
#define PI 3.14159
main()
{ …… }
#undef PI
f1()
{ …… }
表示 PI只在 main函数中有效,在 f1中无效。
4) 宏名在源程序中若用引号括起来,则预处
理程序不对其作宏代换。
【 例 】
#define OK 100
main( )
{ printf("OK");
printf("\n");
}
5)宏定义允许嵌套,在宏定义的字符串中可以使用已经定
义的宏名。在宏展开时由预处理程序层层代换。
例如,
#define PI 3.1415926
#define S PI*y*y /* PI是已定义的宏名 */
对语句,printf("%f",S);在宏代换后变为,
printf("%f",3.1415926*y*y);
6)习惯上宏名用大写字母表示,以便于与变量区
别。但也允许用小写字母,不要理解为宏名只
能用大写字母表示。
带参宏定义
带参宏定义的一般形式为,
#define 宏名 (形参表 ) 字符串
在字符串中含有各个形参。
带参宏调用的一般形式为:宏名 (实参表 );
例如,
#define M(y) y*y+3*y /*宏定义 */
……
k=M(5); /*宏调用 */
……
在宏调用时,用实参 5去代替形参 y,经预处理宏展开后
的语句为,
k=5*5+3*5
带参宏展开的过程:在程序中,如果有带参的宏,则按
宏定义中指定的字符串进行替换:对于串中的形参,用
相应的实参代替,对于不是形参的字符则保留。 【 例 】
#define MAX(a,b) (a>b)?a:b
main( )
{ int x,y,max;
printf("input two numbers,");
scanf("%d%d",&x,&y);
max=MAX(x,y);
printf("max=%d\n",max);
}
max=MAX(x,y)为宏调用,宏展开后该语句为,
max=(x>y)?x:y;
说明,
1,带参宏定义中,宏名和形参表之间不能
有空格出现。
2,在带参宏定义中,形式参数不分配内存
单元,因此不必作类型定义。而宏调用
中的实参有具体的值
3,在宏定义中的形参是标识符,而宏调用
中的实参可以是表达式。
【 例 】
#define SQ(y) (y)*(y)
main(){
int a,sq;
printf("input a number,");
scanf("%d",&a);
sq=SQ(a+1);
printf("sq=%d\n",sq);
}
(y)*(y) 代换 SQ,得到如下语句,
sq=(a+1)*(a+1);
分析:若 #define SQ(y) y*y,则展开的结果如何?
继续分析如下程序的结果,
【 例 】 #define SQ(y) y*y
main( )
{ int a,sq;
printf("input a number,");
scanf("%d",&a);
sq=SQ(a+1);
printf("sq=%d\n",sq); }
运行结果为,
input a number:3
sq=7
宏代换后将得到以下语句,sq=a+1*a+1;
接上例继续分析,【 例 】
#define SQ(y) (y)*(y)
main( )
{int a,sq;
printf("input a number,");
scanf("%d",&a);
sq=160/SQ(a+1);
printf("sq=%d\n",sq); }
运行本程序
如输入值仍为
3时,希望结
果为 10。但实
际运行的结果
如下,
input a
number:3
sq=160
为什么会得这样的结果呢?分析宏调用语句,在
宏代换之后变为,sq=160/(a+1)*(a+1);
a为 3时,由于,/”和
,*”运算符优先
级和结合性相同,
则先作 160/(3+1)得
40,再作 40*(3+1)
最后得 160。为了
得到正确答案应在
宏定义中的整个字
符串外加括号,程
序修改如右,
【 例 】
#define SQ(y) ((y)*(y))
main(){
int a,sq;
printf("input a number,");
scanf("%d",&a);
sq=160/SQ(a+1);
printf("sq=%d\n",sq); }
以上讨论说明,对于宏定义不仅应在参数两侧加括号,也应在整个字符串外加
括号。
5.带参的宏和带参函数很相似,但有本质上的不同,除上
面已谈到的各点外,把同一表达式用函数处理与用宏处
理两者的结果有可能是不同的。
【 例 】
main( ){
int i=1;
while(i<=5)
printf("%d\n",SQ(i++));
}
SQ(int y)
{ return((y)*(y));}
【 例 】
#define SQ(y) ((y)*(y))
main( ){
int i=1;
while(i<=5)
printf("%d\n",SQ(i++));
}
从以上分析可以看出函数
调用和宏调用二者在形式
上相似,在本质上是完全
不同的。
文件包含,文件包含命令行的一般形式为,
#include"文件名 "或 #include<文件名 >
文件包含命令的功能是把指定的文件插入该命
令行位置取代该命令行,从而把指定的文件
和当前的源程序文件连成一个源文件。
说明,1、两种命令形式的区别,
#include"stdio.h"/*先在源文件目录上去找,然
后在 tc\include目录中去找 */
#include<math.h>/*在 tc\include中去找 */
2、一个 include命令只能指定一个被包含文件,
若有多个文件要包含,则需用多个 include命
令。
3、文件包含允许嵌套,即在一个被包含的文
件中又可以包含另一个文件。
什么是条件编译?可以按不同的条件去编译不同的程序
部分,因而产生不同的目标代码文件。这对于程序的移
植和调试是很有用的。
条件编译有三种形式,下面分别介绍,
1.第一种形式,
#ifdef 标识符
程序段 1
#else
程序段 2
#endif
它的功能是:如果标识符已被 #define命令定义过则对程
序段 1进行编译;否则对程序段 2进行编译。如果没有
程序段 2(它为空 ),本格式中的 #else可以没有,即可以
写为,
#ifdef 标识符
程序段
#endif
下面二种形式的功能是:如常量表达式的值为真 (非 0),
则对程序段 1 进行编译,否则对程序段 2进行编译。因此
可以使程序在不同条件下,完成不同的功能。
2.第二种形式,
#ifndef 标识符
程序段 1
#else
程序段 2
#endif
3.第三种形式,
#if 常量表达式
程序段 1
#else
程序段 2
#endif
结束