第7章 编译预处理
第7章编译预处理
“编译预处理”是指在C编译程序对C源程序进行编译之前,对预处理命令进行“预先”处理的过程。编译预处理由编译预处理程序完成。
预处理命令(preprocessor directive)不是C语言组成部分,C语言的编译程序无法识别他们,如C程序中的“#inc lude
C语言提供的编译预处理命令主要有以下三种:
(1)宏定义。
(2)文件包含。
(3)条件编译。
为了与一般的C语句相区别,编译预处理命令必须以“#”为首字符、尾部不能加分号(C语句必须加分号),一行只能写一条编译预处理命令。
编译预处理命令可以出现在源程序中的任何位置,其作用范围是从它出现的位置直到所在源程序文件的末尾。
C的编译预处理功能为程序调试、移植提供了便利,正确使用编译预处理功能可以有效地提高程序的开发效率。
7.1 宏定义
7.1.1不带参数的宏定义
不带参数的宏定义命令形式为:
#define 宏名字符串
其中:宏名为标识符。
功能:在编译预处理时,将程序中在该命令后所有与宏名相同的文本用字符串置换。
例如:
#define PI 3.1415926
它的功能是在程序中用宏名“PI”来代替“3.1415926”这个字符串,在编译预处理时,将程序中在该命令以后出现的所有的“PI”都用“3.1415926”代替。使用宏定义(macro definition),可以用一个简单的名字(宏名)来代替一个较长的字符串,以增加程序的可读性。
在编译预处理时将宏名替换成字符串的过程称为“宏展开”。
关于宏定义和宏展开的说明:
(1) 宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。
(2) 一个宏名只能被定义一次,否则出错,被认为是重复定义。
(3) 在进行宏定义时,可以引用已定义的宏名,可以层层替换。见下面例7.1
(4) 对在字符串常量及用户标识符中与宏名相同的部分不作替换。例如已定义
#define L 1234
在程序中有变量Length,其中L不作替换。同样在printf(“L=”,…)中的L不作替换。
(5) 宏定义的作用域:从定义开始到程序结束。
(6) 当宏定义在一行中写不下,换行时,需要在行尾加换行字符“\”。
下面的例子可以帮助我们理解以上说明:
【例7.1】使用宏定义的例子。
程序如下:
#inc lude
#define R 3.0 /* 定义宏R */
#define PI 3.1415926 /* 定义宏PI */
#define L 2*PI*R /* 可使用已定义的宏名定义新的宏*/
#define S PI*R*R
void main()
{
printf("L=%.2f\nS=%.2f\n",L,S); /* 对字符串常量中的的宏名L和S不作替换*/ }
运行结果如下:
L=18.85
S=28.27
程序说明:经过宏展开后,printf函数中的输出项L被展开为2*3.1415926*3.0,S 被展开为3.1415926*3.0*3.0,printf函数调用语句展开为
printf(“L=%.2f\nS=%.2f\n”, 2*3.1415926*3.0, 3.1415926*3.0*3.0);
7.1.2 带参数的宏定义
带参数的宏定义命令形式为:
#define宏名(形参表) 字符串
形参表中的不同形参之间用逗号隔开,字符串中包含形参表中的参数。
在编译预处理时,将程序中该命令后所有与宏名相同的文本用字符串置换,但置换
时字符串中的形参要用相应的实参置换。例如:
#define M(a,b) a*b
area=M(3,7);
在宏展开时会替换成:area=3*7; 也就是说由实参3替换了形参a,由实参7替换了形参b。
【例7.2】分析下面程序的执行结果。
程序如下:
#inc lude
#define M(x,y) (x*y) /* 带参数的宏定义*/
void main()
{ int a=2,b=3,c,d;
c=M(a,b); /* 宏展开后:c=(a*b) */
d=M(a+1,b+1); /* 宏展开后:d=(a+1*b+1) */
printf("c=%d, d=%d\n",c,d);
}
程序运行结果:
c=6, d=6
程序说明:带参数的宏展开只是简单的替换,没有计算功能,这一点与函数不同。
在使用带参数的宏时,要注意括号的使用,请看下面的例子:
(1) 宏定义:#define MU(x,y) x*y
宏调用:6/MU(2+3,4+5)
宏展开:6/2+3*4+5
(2) 宏定义:#define MU(x,y) (x*y)
宏调用:6/MU(2+3,4+5)
宏展开:6/(2+3*4+5)
(3) 宏定义:#define MU(x,y) (x)*(y)
宏调用:6/MU(2+3,4+5)
宏展开:6/(2+3)*(4+5)
(4) 宏定义:#define MU(x,y) ((x)*(y))
宏调用:6/MU(2+3,4+5)
宏展开:6/((2+3)*(4+5))
带参数的宏在程序中使用时,形式与函数相似,但是在本质上他们是完全不同的。区别在于:
(1) 在程序控制上,函数的调用需要进行程序流程的转移,而使用带参数的宏则仅仅是表达式的运算。
(2) 带参数的宏一般是个表达式,所以它不象函数那样有固定的数据类型。宏的数据类型可以说是它的表达式运算结果的类型,随着使用的实参数值的不同,运算结果呈
现不同的数据类型。
(3) 在调用函数时,对使用的实参有一定的数据类型限制。而带参数的宏的实参可以是任意数据类型。
(4) 函数调用时存在着从实参向形参传递数据的过程。而带参数的宏不存在这种过程。在程序中使用带参数的宏比调用函数可以得到较高的程序执行速度,但是在对源程序进行编译时花费的时间要更多一些。
(5) 带参数的宏除了使用运算表达式定义外,还可以使用函数。在标准函数库中经常使用这种方式。
(6) 宏定义如果使用不当,可能会产生不易觉察的错误。
在程序设计时,经常把那些反复使用的运算表达式定义为带参数的宏。这样一方面使程序更加简洁,另一方面可以使运算的意义更加明显。
定义带参数的宏时,对形式参数的数量没有限制,但是一般情况下以不超过三个为宜。下面给出几个常用的带参数的宏定义:
#define MAX(x,y) ((x>y)?x:y) 求x和y中较大的一个
#define ABS(x) ((x>=0)?x: -x) 求x的绝对值
#define PERCENT(x,y) (100.0*x/y) 求x除以y的百分数值
#define ISODD(x) ((x%2==1)?1:0) 判断x是否为奇数
上面给出的宏定义中,运算表达式里出现的形参都是单纯的形式。在实际应用时,应该根据使用情况加上保证运算顺序的圆括号。
7.1.3 终止宏定义
可以用#undef终止宏定义的作用域,一般形式为:
#undef 宏名
例如:
#define PI 3.14
void main()
…
#undef PI
…
以上PI的作用域从#define PI 3.14命令行开始,到#undef PI命令行结束;从#undef以后PI变成无定义,不再代表3.14了。
7.2 文件包含
文件包含(file inclusion)预处理命令有两种格式:
#include“文件名”
#inc lude<文件名>
功能:在编译当前源文件之前,先执行在此文件中的文件包含命令#include,把命令中指定的另一个源文件的内容插入到该命令所在行开始的一段空间,从而使被插入的文件的内容成为当前源文件的一部分。
执行第1种格式(用双引号将文件名括起来)的命令时,若文件名与文件的路径是一起给出的,则预处理程序只在指定的目录中查找指定的被包含文件,若路径名未给出,则首先在当前目录中查找,如果没有找到,再到系统指定的标准库目录中去查找。
执行第2种格式(用尖括号将文件名括起来)的命令时,则只到系统指定的标准库目录中查找。
由此可知,当文件名不带路径时,使用第一种格式所包含的查找范围要广一些。一般说,如果为调用库函数而用#include命令来包含相关的头文件,则用尖括号,以节省查找时间。如果要包含的是用户自己编写的文件(这种文件一般都在当前目录中),一般用双引号。若文件不在当前目录中,双引号内可给出文件路径。
使用文件包含的方法,可以减少重复性的劳动,有利于程序的维护和修改。在程序设计时,可以把一批常用的符号常量、函数说明、宏定义及某些有用的数据类型说明和类型定义,组织在一个独立的文件中,在程序需要使用到这些信息时,可用#include命令把他们包含到所需的位置上去。从而免去每次使用他们时都要重新定义或说明的麻烦。而对这个文件中的符号常量、函数说明、宏定义修改时,也比较方便,当然它的内容一旦修改,所有引用它的文件就要作相应的修改。
在调用库函数时,一般要用#inc lude命令把对应的头文件包含到引用库函数的程序中来,例如程序中要调用getchar函数,则应在该调用语句所在的文件的头部写上:#include
存放在头文件中的内容,是编译程序对有关的源程序进行编译时,需要用到的一些信息,这些信息一般是:
(1) 符号常量,如在stdio.h文件中有:
#define EOF -l
#define NULL 0
定义了宏名EOF和NULL,在程序被编译之前,程序中的标识符EOF则均被-1置换,标识符NULL均被数字0置换,然后才开始编译。
(2) 函数声明,如在stdio.h文件中有printf函数的原型说明,math.h文件中含有各个数学函数的原型说明。
(3) 宏定义,如在stdio.h中含有宏定义:
#define getchar( ) fgetc(stdin)
(4) 某些有用的数据结构声明和类型定义。
在使用#include命令时还要注意以下几点:
(1) 每个#include命令只能包含一个文件。
(2) 文件包含可以嵌套,即一个被包含的源文件可以包含另一个源文件。
【例7.3】一个程序写在多个源文件中的应用举例。
源文件ex703.c中的内容:
#inc lude
#inc lude"ex703_1.c"
#inc lude"ex703_2.c"
void main()
{printf("%d\n",fun1(3,5));
printf("%d\n",fun2(3,5));
}
源文件ex703_1.c中的内容:
int fun1(int a,int b)
{ return a+b;
}
源文件ex703_2.c中的内容:
int fun2(int a,int b)
{ return a*b;
}
在文件ex703.c中,文件包含预编译命令:
#inc lude"ex703_1.c"
#inc lude"ex703_2.c"
将其他两个文件中的内容插入到文件包含命令所在位置,经过编译预处理后,源文件ex703.c包含的内容如下:
…… /* 文件stdio.h 所包含的内容*/
int fun1(int a,int b) /* 文件ex703_1.c所包含的内容*/
{ return a+b;
}
int fun2(int a,int b) /* 文件ex703_1.c所包含的内容*/
{ return a*b;
}
void main()
{printf("%d\n",fun1(3,5));
printf("%d\n",fun2(3,5));
}
程序说明:经过编译预处理后,在对文件ex703.c的编译过程中,C所处理的是一个完整的程序。
7.3 条件编译
一般情况下,源程序中的所有行都要参加编译。但特殊情况下可能需要根据不同的条件编译源程序中的不同部分,也就是说,源程序中的一部分内容只在满足一定条件才进行编译,或者,当条件成立时去编译一组语句,而当条件不成立时编译另一组语句,这就是“条件编译”。
条件编译命令有以下几种形式。
1.#ifdef 标识符
程序段l
#else
程序段2
#endif
功能:当指定的标识符在此之前已经被“#define”语句定义过,“程序段1”被编译,否则,“程序段2”被编译。其中“#else”部分可以省略,即
#ifdef 标识符
程序段l
#endif
例如,在调试程序时,经常要输出一些所需的信息,一旦调试结束,这些信息就不再需要了。一种处理方法可以采用条件编译来完成,只要在源程序中插入类似于以下的语句。
#ifdef DEBUG
printf(“a=%d, b=%d\n”,a,b);
#endif
只要在程序的起始处有以下命令行
#define DEBUG
程序在运行中就会输出a、b的值,以便调试程序时分析。
只要程序中需要输出的地方,都可以使用相同的方法来输出所需的信息。调试结束后,只要将“#define DEBUG”语句删除即可,而不必去一一删除相关的“printf”语句,这样可以简化调试工作。
2.#ifndef标识符
程序段l
#else
程序段2
#endif
功能:当指定的标识符在此之前没有被“#deifne”语句定义过,“程序段1”被编译,否则,“程序段2”被编译。类似于#ifdef,“#else”部分可以省略。
3.#if 表达式
程序段1
#else
程序段2
#endif
功能:如果指定的表达式的值为真(非0),则编译程序段1,否则编译程序段2。
习题
1.单项选择题
(1) 以下叙述中正确的是()。
A) 用#include包含的头文件的后缀不可以是“.a”
B) 若一些源程序中包含某个头文件;当该头文件有错时,只需对该头文件进行修改,包含此头文件所有源程序不必重新进行编译
C) 宏命令行可以看做是一行C语句
D) C编译中的预处理是在编译之前进行的
(2) 程序中调用了库函数exit,必须包含头文件( )。
A)string.h B) stdlib.h C) ctype.h D) math.h
(3) 下列宏定义命令中,哪—个格式是正确的( )。
A)#define pi=3.14159;B)definepi=3.14159
C)#define pi="3.14159" D)#define pi (3.14159)
(4) 宏定义为#define div(x,y) x/y;
语句“printf(“div(x,y)=%d\n”, div(x+3,y-3));”作宏替换后为( )。
A) printf(”x/y%d\n”,(x+3)/(y-3));
B) printf(”x/y=%d\n”, x+3/y-3);
C) printf(”div(x,y)=%d ”,x+3/y-3;);
D) printf(”x/y=%d\n”,x+3/y-3;);
(5) 定义带参数的宏计算两个表达式的乘积,下列定义中哪个是正确的( )。
A)#define mult(u,v) u*v B)#define mult(u,v) u*v;
C)#define mult(u,v) (u)*(v) D)#define mult(u,v)=(u)*(v)
(6) 程序中调用了库函数strcmp,必须包含头文件( )。
A)stdlib.h B) math.h C) ctype.h D) string.h
(7) 以下程序的输出结果是()。
#define MIN(x,y) (x)<(y)?(x):(y)
main()
{ int i,j,k;
i=10; j=15; k=10*MIN(i,j);
printf("%d\n",k);
}
A) 15 B) 100 C) 10 D) 150
(8) 以下程序中的for循环执行的次数是()。
#define N 2
#define M N+1
#define NUM (M+1)*M/2
main()
{ int i;
for(i=1; i<=NUM; i++);
}
A) 5 B) 6 C) 8 D) 9
(9) 以下程序的输出结果是()。
#include "stdio.h"
#define FUDGF(y) 2.84+y
#define PR(a) printf("%d",(int)(a))
#define PRINT1(a) PR(a); putchar('\n')
main()
{ int x=2;
PRINT1(FUDGF(5)*x);
}
A) 11 B) 12 C) 13 D) 15
2.填空题。
(1) 定义—个宏,用于判断所给出的年份year是否为闰年:
#define LEAP_YEAR(y) ______________
(2) 定义带参数的宏为
#define max(a,b) ((a)>(b)?(a):(b))
对表达式max(a,max(b,max(c,d)))作宏替换为(用文字描述)_______________。
(3) 定义一个带参数的宏,若变量中的字符为大写字母则转换成小写字母________。
(4) 以下程序完成a、b的互换,补全程序。
#inc lude
_______________________________
void main()
{ int a,b;
scanf("%d,%d",&a,&b);
___________________
printf("%d,%d\n",a,b);
}
3.编程题。
(1) 输入两个整数,求它们相除的余数。用带参数的宏来实现。
(2) 输入5个数后,输出其中绝对值最小的数。要求定义带参数的宏,计算三个数中绝对值最小的数。
Fluent_UDF_第七章_UDF的编译与链接
第七章 UDF的编译与链接 编写好UDF件(详见第三章)后,接下来则准备编译(或链接)它。在7.2或7.3节中指导将用户编写好的UDF如何解释、编译成为共享目标库的UDF。 _ 第 7.1 节: 介绍 _ 第 7.2 节: 解释 UDF _ 第 7.3 节: 编译 UDF 7.1 介绍 解释的UDF和编译的UDF其源码产生途径及编译过程产生的结果代码是不同的。编译后的UDF由C语言系统的编译器编译成本地目标码。这一过程须在FLUENT运行前完成。在FLUENT运行时会执行存放于共享库里的目标码,这一过程称为“动态装载”。 另一方面,解释的UDF被编译成与体系结构无关的中间代码或伪码。这一代码调用时是在内部模拟器或解释器上运行。与体系结构无关的代码牺牲了程序性能,但其UDF可易于共享在不同的结构体系之间,即操作系统和FLUENT版本中。如果执行速度是所关心的,UDF文件可以不用修改直接在编译模式里运行。 为了区别这种不同,在FLUENT中解释UDF和编译UDF的控制面板其形式是不同的。解释UDF的控制面板里有个“Compile按钮”,当点击“Compile按钮”时会实时编译源码。编译UDF的控制面板里有个“Open按钮”,当点击“Open按钮” 时会“打开”或连接目标代码库运行FLUENT(此时在运行FLUENT之前需要编译好目标码)。 当FLUENT程序运行中链接一个已编译好的UDF库时,和该共享库相关的东西都被存放到case文件中。因此,只要读取case文件,这个库会自动地链接到FLUENT 处理过程。同样地,一个已经经过解释的UDF文件在运行时刻被编译,用户自定义的C函数的名称与内容将会被存放到用户的case文件中。只要读取这个case文件,这些函数会被自动编译。 注:已编译的UDF所用到的目标代码库必须适用于当前所使用的计算机体系结构、操作系统以及FLUENT软件的可执行版本。一旦用户的FLUENT升级、操作系统改变了或者运行在不同的类型的计算机,必须重新编译这些库。
编译预处理
第九章编译预处理 9.1 选择题 【题9.1】以下叙述中不正确的是。 A)预处理命令行都必须以#号开始 B)在程序中凡是以#号开始的语句行都是预处理命令行 C)C程序在执行过程中对预处理命令行进行处理 D)以下是正确的宏定义 #define IBM_PC 【题9.2】以下叙述中正确的是。 A)在程序的一行上可以出现多个有效的预处理命令行 B)使用带参的宏时,参数的类型应与宏定义时的一致 C)宏替换不占用运行时间,只占编译时间 D)在以下定义中C R是称为“宏名”的标识符 #define C R 045 【题9.3】请读程序: #define ADD(x) x+x main() { int m=1,n=2,k=3; int sum=ADD(m+n)*k; printf(“sum=%d”,sum); } 上面程序的运行结果是。 A)sum=9 B)sum=10 C)sum=12 D)sum=18 【题9.4】以下程序的运行结果是。 #define MIN(x,y) (x)<(y)?(x):(y) main() { int i=10,j=15,k; k=10*MIN(i,j); printf(“%d\n”,k); } A)10 B)15 C)100 D)150 【题9.5】在宏定义#define PI 3.14159中,用宏名PI代替一个。 A)常量B)单精度数C)双精度数D)字符串
【题9.6】以下程序的运行结果是。 #include
C语言第1章至第七章
第1章C语言概述 本章要点 了解C语言的特点,初步掌握C语言的基本结构,掌握在VC++6.0环境下C语言程序的编辑、编译、连接与运行,了解算法的概念及用传统流程图表示算法的方法。本章的重点和难点是VC++6.0集成环境下C 语言程序的运行方法。 第一节C语言的发展与特点 一、C语言的发展 C语言诞生以前,系统软件主要是用汇编语言编写的。但由于汇编语言依赖于计算机硬件,其可读性和可移植性都很差,而一般的高级语言又难以实现对计算机硬件直接进行操作。所以人们希望出现一种兼有汇编语言和高级语言优点的新语言,于是诞生了C语言。 C语言是由贝尔实验室的Dennis Ritchie在20世纪70年代初发明的,最初是作为UNIX 系统的开发语言。70年代末,随着微型计算机的发展,C语言开始移植到非UNIX环境中,并逐步成为独立的程序设计语言。1978年,Brian.W.Kernighan和Dennis.M.Ritchie出版了名著《The C Programming Language》,通常简称为《K&R》,该书中介绍的C语言成为后来广泛使用的C语言版本的基础,也有人称之为《K&R》标准。但是,在《K&R》中并没有定义一个完整的标准C语言。继C语言问世之后出现了许多版本,由于没有统一的标准,不同C语言版本之间缺乏兼容。为了改变这种情况,美国国家标准化协会ANSI(American National Standards Institute)于1983年根据C语言问世以来各种版本对C语言的发展和扩充,公布了第一个C语言标准草案(’83 ANSI C)。1989年,ANSI公布了一个完整的C语言标准,常称ANSI C或C89,该标准定义了语言和一个标准C库。1990年,C89被国际标准化组织ISO (International Standard Organization)接受作为国际标准,常称C90,C89和C90实质上是同一个标准。1999年,ISO对C语言标准进行修订,在基本保留原来的C语言特征的基础上,针对应用的需要,增加了一些功能,尤其是C++中的一些功能,命名为C99。本书基本上以C89为基础进行介绍。 二、C语言的特点 C语言是一种用途广泛、功能强大、使用灵活的结构化程序设计语言。由于以下原因而在程序员中流行: 1.C语言具有结构化的高级编程语言应有的所有高级指令,使程序员不需要知道硬件细节。同时,C也具有一些低级指令,允许程序员能够直接快速地访问硬件。这种把高级语言的基本结构和语句与低级语言的实用性结合起来的特点使得C语言在通用程序设计和系统程序设计上都能满足程序员的需要。
C语言程序设计教案 第九章 编译预处理
第九章编译预处理 课题:第九章编译预处理 教学目的:1、了解预处理的概念及特点 2、掌握有参宏与无参宏的定义及使用,领会文件包含的使用及效果 教学重点:教学难点:掌握宏的使用,文件包含有参宏与无参宏的使用 步骤一复习引导 ANSI C标准规定可以在C源程序中加入一些“预处理命令”,以改进程序设计环境,提高编程效率。 这些预处理命令是由ANSI C统一规定的,但它不是C语言本身的组成部分,不能直接对它们进行编译。必须在对程序进行通常的编译之前,先对程序中这些特殊的命令进行“预处理”,即根据预处理命令对程序作相应的处理。经过预处理后程序不再包括预处理命令了,最后再由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。 步骤二讲授新课 C语言与其他高级语言的一个重要区别是可以使用预处理命令和具有预处理的功能。C 提供的预处理功能主要有以下三种:宏定义、文件包含、条件编译。 分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“ #” 开头。 §9.1宏定义 宏:代表一个字符串的标识符。 宏名:被定义为“宏”的标识符。 宏代换(展开):在编译预处理时,对程序中所有出现的“宏名”,用宏定义中的字符串去代换的过程。 一、不带参数的宏定义 一般形式:#define 标识符字符串 #define PI 3.1415926 main() { float l, s, r, v; printf( “input radius:” ); scanf( “%f”, &r ); l = 2.0*PI*r; s = PI*r*r; v = 3.0/4*PI*r*r*r; printf(“%10.4f,%10.4f,%10.4\n”, l, s, v); }
C语言题库第7章宏定义与预处理
第七章宏定义与预处理一.单项选择 1. 以下程序的运行结果是( D )。 #include
A.75 B.125 C.76 D.126 4. 以下程序的运行结果是( C )。 #include
第7章-编译预处理
一、判断题 1. 宏替换时先求出实参表达式的值,然后带入形参运算求值。 答案:F 2. 宏替换不存在类型问题,它的参数也是无类型的。 答案:T 3. 在C++语言标准库头文件中包含了许多系统函数的原型声明,因此只要程序中使用了这些函数,则应包含这些头文件。 答案:T 4. H头文件只能由编译系统提供。 答案:F 5. #include命令可以包含一个含有函数定义的C++语言源程序文件。 答案:T 6. 用#include包含的头文件的后缀必须是.h。 答案:F 7. #include “C:\\USER\\F1.H”是正确的包含命令,表示文件F1.H存放在C盘的USER 目录下。 答案:F 8. #include <…>命令中的文件名是不能包括路径的。 答案:F 9. 可以使用条件编译命令来选择某部分程序是否被编译。 答案:T 10.在软件开发中,常用条件编译命令来形成程序的调试或正式版本。 答案:T 二、选择题 1. 以下叙述中错误的是()。 A.预处理命令行都必须以#开始 B. 在程序中凡是以#开始的语句都是预处理命令行 C. C++程序在执行过程中对预处理命令行进行处理 D. 预处理命令行可以出现在C++程序中任意一行上 答案:C 2. 以下有关宏替换的叙述中错误的是()。 A.宏替换不占用运行时间 B. 宏名无类型 C. 宏替换只是字符替换 D. 宏名必须用大写字母表示 答案:D 3. 在编译指令中,宏定义使用()指令。 A.#include B. #define C. #if D. #else
答案:B 4. 社#define P(x) x/x,执行语句cout<
第六讲编译预处理
第6讲编译预处理 6.1 编译预处理的根概念和特点 预处理——是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。 6.1.2 编译预处理的特点 ⑴预处理命令均以“#”号开头,在它前面不能出现空格以外的其他字符。 ⑵每一行命令独占一行。 ⑶命令不以“;”为结束符,它不是C语句。 ⑷预处理程序控制行的作用范围仅限于说明它们的那个文件。 ◇C语言提供的预处理功能有:宏定义、文件包含、条件编译等。 ◇合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。 6.2 宏定义 宏——在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。 宏名——被定义为“宏”的标识符称为“宏名”。 宏代换(宏替换或宏展开)——在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。 6.2.1 不带参的宏定义 其定义的一般形式为:
#define 标识符字符串 ◇在前面介绍过的符号常量的定义就是一种无参宏定义。 #define PI 3.14.5926 ◇常对程序中反复使用的表达式进行宏定义。 例如: #define M (y*y+3*y) 用标识符M来代替表达式(y*y+3*y)。 在编写源程序时,所有的(y*y+3*y)都可由M代替。【例】 #include
第7章 编译预处理
第7章编译预处理 “编译预处理”是指在C编译程序对C源程序进行编译之前,对预处理命令进行“预先”处理的过程。编译预处理由编译预处理程序完成。 预处理命令(preprocessor directive)不是C语言组成部分,C语言的编译程序无法识别他们,如C程序中的“#inc lude
第九章编译预处理
第九章 编译预处理 编译指令(编译预处理指令):C 源程序除了包含程序命令(语句)外,还可以使用各种编译指令(编译预处理指令)。编译指令(编译预处理指令)是给编译器的工作指令。这些编译指令通知编译器在编译工作开始之前对源程序进行某些处理。编译指令都是用“#”引导。 编译预处理:编译前根据编译预处理指令对源程序的一些处理工作。C 语言编译预处理主要包括宏定义、文件包含、条件编译。 编译工作实际分为两个阶段:编译预处理、编译。广义的编译工作还包括连接。 9、1 宏定义 宏定义:用标识符来代表一个字符串(给字符串取个名字)。C 语言用“#define ”进行宏定义。C 编译系统在编译前将这些标识符替换成所定义的字符串。 宏定义分为不带参数的宏定义和带参数宏定义。 9、1、1 不带参数宏定义(简单替换) 1 其中:标识符-宏名。 2、宏调用:在程序中用宏名替代字符串。 3、宏展开:编译预处理时将字符串替换宏名的过程,称为宏展开。
说明: (1)宏名遵循标识符规定,习惯用大写字母表示,以便区别普通的变量。 (2)#define 之间不留空格,宏名两侧空格(至少一个)分隔。 (3)宏定义字符串不要以分号结束,否则分号也作为字符串的一部分参加展开。从这点上 看宏展开实际上是简单的替换。 例如:#define PI 3.14; 展开为s=3.14;*r*r ;(导致编译错误) (4)宏定义用宏名代替一个字符串,并不管它的数据类型是什么,也不管宏展开后的词法 和语法的正确性,只是简单的替换。是否正确,编译时由编译器判断。 例如:#define PI 3.I4 照样进行宏展开(替换),是否正确,由编译器来判断。 (5)#define 宏定义宏名的作用范围从定义命令开始直到本源程序文件结束。可以通过 #undef 终止宏名的作用域。
第七章网站测试题
Question1 对于以下宏定义: #define M(x)x*x #define N(x,y)M(x)+M(y)宏调用N(2,2+5)执行后,值为_________。 选择一个答案 A. 53 B. 21 C. 19 D. 51 Qusetion2 如果一个函数定义后不希望别的文件使用,则在该函数首部前增加一个关键字_______ 选择一个答案 a. const b. extern c. static d. auto Question 3 在文件包含预处理语句(#include )的使用形式中,当之后的文件名用<>(尖括号)括起时,寻找被包含文件的方式是。选择一个答案 A. 仅仅搜索源程序所在目录 B. 仅仅搜索当前目录 C. 直接按系统设定的标准方式搜索目录 D. 先在源程序所在目录搜索,再按系统设定的标准方式搜索 Question 4 以下不正确的叙述是。 选择一个答案 A. C ++程序在执行过程中对预处理命令进行处理 B. #define ABCD 是正确的宏定义
C. 预处理命令行都必须以"#" 开始 D. 在程序中凡是以"#" 开始的语句行都是预处理命令行 Question 5 关于外部变量的下列说法错误的是_______ 选择一个答案 a. 外部变量只需要在一个文件中定义一次,但可以其他文件中多次声明 b. 只要在主函数外面定义的变量就一定是外部变量 c. 外部变量在其他文件中被使用时需要用extern进行事先声明 d. 函数外定义的变量,其前若用static修饰,则不能作为外部变量 Question 6 以下不正确的叙述是。 选择一个答案 A. 宏替换不占用运行时间 B. 宏替换只是字符替换 C. 宏名无类型 D. 宏名必须用大写字母表示 Question 7 下列哪一个不是模块化程序设计应具有的特点__________。 选择一个答案 A. 开发一个模块不需要知道系统其他模块的内部结构和编程细节 B. 具有可修改性。对系统的一次修改只涉及少数几个模块 C. 鼓励多使用外部变量,以减少模块之间的相互参数传递 D. 模块之间的接口尽可能简明 Question 8 关于编译预处理,下列说法正确的是_________。 选择一个答案 A. 用户自定义头文件时前后加条件编译指令可以避免重复包含。
补充习题(编译预处理)
编译预处理 一、选择题 1、以下叙述中正确的是() (A)用#include包含的头文件的后缀不可以是”.a” (B)若一些源程序中包含某个头文件,当该头文件有错时,只需对该头文件进行修改,包含此头文件的所有源程序不必重新进行编译(C)宏命令行可以看做是一行C语句 (D)C编译中的预处理是在编译之前进行的 2、下面是对宏定义的描述,不正确的是() (A)宏不存在类型问题,宏名无类型,它的参数也无类型 (B)宏替换不占用运行时间 (C)宏替换时先求出实参表达式的值,然后代入形参运算求值 (D)宏替换只不过是字符替代而已 3、以下程序的输出结果是() #include
#define N 2 #define M N+1 #define NUM (M+1)*M/2 main() {int i; for(i=1;i<=NUM;i++); printf(“%d\n”,i);} (A)5 (B)6 (C)8 (D)9 6、以下程序的输出结果是() #define f(x) x*x #include
第九章预处理的考试试题
(1) 下面叙述错误的是()。 A) “#define PRICE 30”命令的作用是定义一个与30等价的符号常量PRICE B) C源程序中加入一些预处理命令是为了改进程序设计环境,提高编程效率 C) “#include
A、第一维和第二维都为4 B、第一维和第二维都为3 C、第一维为4,第二维为3 D、第一维为3,第二维为4 (7) 下列程序的运行结果是()。 # define M 5 # define N M+1 # define NN N*N-M void main( ) { printf(“%d\n”,3*NN); } A、108 B、16 C、21 D、103 (8) 下列叙述正确的是()。 A、宏定义中的宏名必须用大写字母表示 B、为提高程序运行速度可在源程序中加入一些宏定义 C、一个C语言源程序只能有一条预处理命令 D、宏定义不占用程序运行时间,但与程序中的语句一样需要编译 (9) 下列叙述错误的是()。 A、宏定义可出现在源程序中任意合适的位置,且必须在行末加分号 B、预处理命令行都必须以#号开始 C、C语言源程序中加入一些预处理命令是为了改进程序设计环境,提高编程效率 D、# define PER 1.5的作用是用标识符PER代表1.5 (10) 下列程序的运行结果是()。 # define K 5 void main( ) { int a=3,b=4; printf(“%d\n”,K*(a+b)); } A、20 B、15 C、5 D、35 (11) 以下叙述正确的是()。 A) 一个源程序只能由一个编译预处理命令行 B) 编译预处理命令都必须以"#"开头 C) "#define PRICE=30"定义了与30等价的符号常量PRICE
第八章 编译预处理
第九章编译预处理 预处理功能主要有三种:宏定义;文件包含;条件编译。 9.1宏替换(宏定义) 9.1.1不带参数的宏定义 1. #define 指令:用一个指定的标识符来代表一个字符串。 2.定义的一般形式是: #define 宏名字符串(或数值) 由#define指令定义后, 在程序中每次遇到该宏名时就用所定义的字符串(或数值)代替它。 例1:#define PI 3.14159265 #define RADIUS 2.0 double circum() { return(2.0*PI*RADIUS); } double area() { return(PI*RADIUS*RADIUS); } main() { printf("L=%lf\n",circum()); printf("S=%lf\n",area()); } 注意: ①在宏定义的后面没有";"(因为它不是语句) ②习惯上用大写字符作为宏名, 与变量名相区别,而且常放在程序开头。 ③使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量: #define array_size 1000 int array[attay_size]; ④可以用#undef命令终止宏定义的作用域。 #define G 9.8 main() { } #undef G f1( )
…….. ⑤在进行宏定义时,可以引用已定义的宠名,可以层层置换。 例:#define R 3.0 #define PI 3.1415926 #define L 2*PI*R #define S PI*R*R main() { printf(“L=%f\ns=%f\n”,L,S); } ⑥在程序中用双引号括起来的字符串骨近字符,与宏名相同,不进行置换,作为字符串。 9.1.2带参数的宏定义 1.# define 宏名(参数)字符串 不是进行简单的字符串替换,还要进行参数替换。 例2: #define MAX(x, y) ((x)>(y))?(x):(y) main() { int i=10, j=15; printf("The Maxmum is %d", MAX(i, j)); } 展开相当于: printf("The Maxmum is %d", ((i)>(j))?(i):(j)); 9.2.“文件包含”处里(#include) 1.定义:#include 指令的作用是指示编译器将该指令所指出的另一个源文件嵌入#include指令所在的程序中.其一般形式为:#include <文件名> 或#include “文件名” 第一种形式:系统到系统标准目录中搜索该文件 第二种形式:首先到当前目录中搜索该文件,如找不到则到系统标准目录中去搜索该文件。 例3: 输入一个句子,统计单词个数。 首先编写一个头文件: /*hong.h*/ #include