C语言程序设计第九章 预处理命令

C语言程序设计第九章 预处理命令
C语言程序设计第九章 预处理命令

9 预处理命令 (1)

9.1 概述 (1)

9.2 宏定义 (1)

9.2.1 无参宏定义 (1)

9.2.2 带参宏定义 (4)

9.3 文件包含 (8)

9.4 条件编译 (9)

9.5 本章小结 (11)

9预处理命令

9.1 概述

在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分。

所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。

C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。本章介绍常用的几种预处理功能。

9.2 宏定义

在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。

宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。

在C语言中,“宏”分为有参数和无参数两种。下面分别讨论这两种“宏”的定义和调用。

9.2.1无参宏定义

无参宏的宏名后不带参数。

其定义的一般形式为:

#define 标识符字符串

其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。

在前面介绍过的符号常量的定义就是一种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。

例如:

#define M (y*y+3*y)

它的作用是指定标识符M来代替表达式(y*y+3*y)。在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序作编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去置换所有的宏名M,然后再进行编译。

【例9.1】

#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);

}

上例程序中首先进行宏定义,定义M来替代表达式(y*y+3*y),在s=3*M+4*M+5* M中作了宏调用。在预处理时经宏展开后该语句变为:

s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);

但要注意的是,在宏定义中表达式(y*y+3*y)两边的括号不能少。否则会发生错误。如当作以下定义后:

#difine M y*y+3*y

在宏展开时将得到下述语句:

s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y;

这相当于:

3y2+3y+4y2+3y+5y2+3y;

显然与原题意要求不符。计算结果当然是错误的。因此在作宏定义时必须十分注意。应保证在宏代换之后不发生错误。

对于宏定义还要说明以下几点:

1)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一

种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。

2)宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。

3)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作

用域可使用# undef命令。

例如:

#define PI 3.14159

main()

{

……

}

#undef PI

f1()

{

……

}

表示PI只在main函数中有效,在f1中无效。

4)宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。

【例9.2】

#define OK 100

main()

{

printf("OK");

printf("\n");

}

上例中定义宏名OK表示100,但在printf语句中OK被引号括起来,因此不作宏代换。程序的运行结果为:OK这表示把“OK”当字符串处理。

5)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预

处理程序层层代换。

例如:

#define PI 3.1415926

#define S PI*y*y /* PI是已定义的宏名*/

对语句:

printf("%f",S);

在宏代换后变为:

printf("%f",3.1415926*y*y);

6)习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

7)可用宏定义表示数据类型,使书写方便。

例如:

#define STU struct stu

在程序中可用STU作变量说明:

STU body[5],*p;

#define INTEGER int

在程序中即可用INTEGER作整型变量说明:

INTEGER a,b;

应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。

宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。请看下面的例子:

#define PIN1 int *

typedef (int *) PIN2;

从形式上看这两者相似,但在实际使用中却不相同。

下面用PIN1,PIN2说明变量时就可以看出它们的区别:

PIN1 a,b;在宏代换后变成:

int *a,b;

表示a是指向整型的指针变量,而b是整型变量。

然而:

PIN2 a,b;

表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型,但毕竟是作字符代换。在使用时要分外小心,以避出错。

8)对“输出格式”作宏定义,可以减少书写麻烦。

【例9.3】中就采用了这种方法。

#define P printf

#define D "%d\n"

#define F "%f\n"

main(){

int a=5, c=8, e=11;

float b=3.8, d=9.7, f=21.08;

P(D F,a,b);

P(D F,c,d);

P(D F,e,f);

}

9.2.2带参宏定义

C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。

对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。

带参宏定义的一般形式为:

#define 宏名(形参表) 字符串

在字符串中含有各个形参。

带参宏调用的一般形式为:

宏名(实参表);

例如:

#define M(y) y*y+3*y /*宏定义*/

……

k=M(5); /*宏调用*/

……

在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:

k=5*5+3*5

【例9.4】

#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表示条件表达式(a>b)?a:b,形参a,b 均出现在条件表达式中。程序第七行max=MAX(x,y)为宏调用,实参x,y,将代换形参a,b。宏展开后该语句为:

max=(x>y)?x:y;

用于计算x,y中的大数。

对于带参的宏定义有以下问题需要说明:

1. 带参宏定义中,宏名和形参表之间不能有空格出现。

例如把:

#define MAX(a,b) (a>b)?a:b

写为:

#define MAX (a,b) (a>b)?a:b

将被认为是无参宏定义,宏名MAX代表字符串 (a,b) (a>b)?a:b。宏展开时,宏调用语句:

max=MAX(x,y);

将变为:

max=(a,b)(a>b)?a:b(x,y);

这显然是错误的。

2. 在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。

3. 在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。

【例9.5】

#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。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y) 代换SQ,得到如下语句:

sq=(a+1)*(a+1);

这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。而宏代换中对实参表达式不作计算直接地照原样代换。

4. 在宏定义中,字符串内的形参通常要用括号括起来以避免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。如果去掉括号,把程序改为以下形式:

【例9.6】

#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

同样输入3,但结果却是不一样的。问题在哪里呢? 这是由于代换只作符号代换而不作其它处理而造成的。宏代换后将得到以下语句:

sq=a+1*a+1;

由于a为3故sq的值为7。这显然与题意相违,因此参数两边的括号是不能少的。即使在参数两边加括号还是不够的,请看下面程序:

【例9.7】

#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);

}

本程序与前例相比,只把宏调用语句改为:

sq=160/SQ(a+1);

运行本程序如输入值仍为3时,希望结果为10。但实际运行的结果如下:input a number:3

sq=160

为什么会得这样的结果呢?分析宏调用语句,在宏代换之后变为:

sq=160/(a+1)*(a+1);

a为3时,由于“/”和“*”运算符优先级和结合性相同,则先作160/(3+1)得40,再作40*(3+1)最后得160。为了得到正确答案应在宏定义中的整个字符串外加括号,程序修改如下:

【例9.8】

#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.带参的宏和带参函数很相似,但有本质上的不同,除上面已谈到的各点外,把同一

表达式用函数处理与用宏处理两者的结果有可能是不同的。

【例9.9】

main(){

int i=1;

while(i<=5)

printf("%d\n",SQ(i++));

}

SQ(int y)

{

return((y)*(y));

}

【例9.10】

#define SQ(y) ((y)*(y))

main(){

int i=1;

while(i<=5)

printf("%d\n",SQ(i++));

}

在例9.9中函数名为SQ,形参为Y,函数体表达式为((y)*(y))。在例9.10中宏名为SQ,形参也为y,字符串表达式为(y)*(y))。例9.9的函数调用为SQ(i++),例9.10的宏调用为SQ(i++),实参也是相同的。从输出结果来看,却大不相同。

分析如下:在例9.9中,函数调用是把实参i值传给形参y后自增1。然后输出函数值。因而要循环5次。输出1~5的平方值。而在例9.10中宏调用时,只作代换。SQ(i++)被代换为((i++)*(i++))。在第一次循环时,由于i等于1,其计算过程为:表达式中前一个i初值为1,然后i自增1变为2,因此表达式中第2个i初值为2,两相乘的结果也为2,然后i 值再自增1,得3。在第二次循环时,i值已有初值为3,因此表达式中前一个i为3,后一个i为4,乘积为12,然后i再自增1变为5。进入第三次循环,由于i 值已为5,所以这将是最后一次循环。计算表达式的值为5*6等于30。i值再自增1变为6,不再满足循环条件,停止循环。

从以上分析可以看出函数调用和宏调用二者在形式上相似,在本质上是完全不同的。

6.宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。看下

面的例子。

【例9.11】

#define SSSV(s1,s2,s3,v) s1=l*w;s2=l*h;s3=w*h;v=w*l*h;

main(){

int l=3,w=4,h=5,sa,sb,sc,vv;

SSSV(sa,sb,sc,vv);

printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);

}

程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4 个形参分别为4个赋值符左部的变量。在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。

9.3 文件包含

文件包含是C预处理程序的另一个重要功能。

文件包含命令行的一般形式为:

#include"文件名"

在前面我们已多次用此命令包含过库函数的头文件。例如:

#include"stdio.h"

#include"math.h"

文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。

在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。

对文件包含命令还要说明以下几点:

1.包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。例如以下写法

都是允许的:

#include"stdio.h"

#include

但是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;

使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。

2.一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个

include命令。

3.文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。

9.4 条件编译

预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。

条件编译有三种形式,下面分别介绍:

1.第一种形式:

#ifdef 标识符

程序段1

#else

程序段2

#endif

它的功能是,如果标识符已被 #define命令定义过则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),本格式中的#else可以没有,即可以写为:

#ifdef 标识符

程序段

#endif

【例9.12】

#define NUM ok

main(){

struct stu

{

int num;

char *name;

char sex;

float score;

} *ps;

ps=(struct stu*)malloc(sizeof(struct stu));

ps->num=102;

ps->name="Zhang ping";

ps->sex='M';

ps->score=62.5;

#ifdef NUM

printf("Number=%d\nScore=%f\n",ps->num,ps->score);

#else

printf("Name=%s\nSex=%c\n",ps->name,ps->sex);

#endif

free(ps);

}

由于在程序的第16行插入了条件编译预处理命令,因此要根据NUM是否被定义过来决定编译那一个printf语句。而在程序的第一行已对NUM作过宏定义,因此应对第一个printf

语句作编译故运行结果是输出了学号和成绩。

在程序的第一行宏定义中,定义NUM表示字符串OK,其实也可以为任何字符串,甚至不给出任何字符串,写为:

#define NUM

也具有同样的意义。只有取消程序的第一行才会去编译第二个printf语句。读者可上机试作。

2.第二种形式:

#ifndef 标识符

程序段1

#else

程序段2

#endif

与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。

3.第三种形式:

#if 常量表达式

程序段1

#else

程序段2

#endif

它的功能是,如常量表达式的值为真(非0),则对程序段1 进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能。

【例9.13】

#define R 1

main(){

float c,r,s;

printf ("input a number: ");

scanf("%f",&c);

#if R

r=3.14159*c*c;

printf("area of round is: %f\n",r);

#else

s=c*c;

printf("area of square is: %f\n",s);

#endif

}

本例中采用了第三种形式的条件编译。在程序第一行宏定义中,定义R为1,因此在条件编译时,常量表达式的值为真,故计算并输出圆面积。

上面介绍的条件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。

9.5 本章小结

1.预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程

序员在程序中用预处理命令来调用这些功能。

2.宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在

宏调用中将用该字符串代换宏名。

3.宏定义可以带有参数,宏调用时是以实参代换形参。而不是“值传送”。

4.为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两

边也应加括号。

5.文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,

结果将生成一个目标文件。

6.条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了

内存的开销并提高了程序的效率。

7.使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。

编译预处理

第九章编译预处理 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 #define FUDGE(y) 2.84+y #define PR(a) printf(“%d”,(int)(a)) #define PRINT1(a) PR(a); putchar(‘\n’) main() { int x=2; PRINT1(FUDGE(5)*x); } A)11 B)12 C)13 D)15 【题9.7】以下有关宏替换的叙述不正确的是。 A)宏替换不占用运行时间B)宏名无类型 C)宏替换只是字符替换D)宏名必须用大写字母表示 【题9.8】C语言的编译系统对宏命令的处理是。 A)在程序运行时进行的 B)在程序连接时进行的 C)和C程序中的其它语句同时进行编译的 D)在对源程序中其它成份正式编译之前进行的 【题9.9】若有宏定义如下: #define X 5 #define Y X+1 #define Z Y*X/2 则执行以下printf语句后,输出结果是。 int a; a=Y; printf(“%d\n”,Z); printf(“%d\n”,--a); A)7 B)12 C)12 D)7 6 6 5 5 【题9.10】若有以下宏定义如下: #define N 2 #define Y(n) ((N+1)*n) 则执行语句z=2*(N+Y(5));后的结果是。 A)语句有错误B)z=34 C)z=70 D)z无定值 【题9.11】若有宏定义:#define MOD(x,y) x%y 则执行以下语句后的输出为。 int z,a=15,b=100; z=MOD(b,a); printf(“%d\n”,z++);

第九章 编译预处理

第九章编译预处理 一、单选题 1.以下对宏替换的叙述不正确的是 A)宏替换只是字符的替换B)宏替换不占运行时间 C)宏名无类型,其参数也无类型 D)带参的宏替换在替换时,先求出实参表达式的值,然后代入形参运算求值2.宏定义#define PI 3.14中的宏名PI代替 A)一个单精度实数)B)一个双精度实数 C)一个字符串 D)不确定类型的数 3.有以下宏定义 #define k 2 #define X(k) ((k+1)*k) 当C程序中的语句y = 2 * (K + X(5));被执行后, A)y中的值不确定 B)y中的值为65 C)语句报错 D)y中的值为34 4.以下程序的输出结果是 #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 5.以下程序中的for循环执行的次数是 #define N 2 #define M N + 1 #define NUM (M + 1) * M / 2 main() { int i; for(i = 1; i <= NUM; i++); pritnf(“%d\n”, i ); } A)5 B)6 C)8 D)9 6.以下程序的输出结果是 #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; PRINTF1(FUDGF(5) * X); } A)11 B)12 C)13 D)15 7.以下程序的输出结果是 #define FMT “%d,” main()

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); }

第9章 预处理命令

第9章预处理命令 宏定义不是C语句,所以不能在行尾加分号。如果加了分号则会连分号一起进行臵换。 可以用#undef命令终止宏定义的作用域。 对程序中用“”括起来的内容(即字符串内的字符),即使与宏名相同,也不进行臵换。宏定义只做字符替换,不分配内存空间。 宏名不是变量,不分配存储空间,也不能对其进行赋值。 在宏展开时,预处理程序仅对宏名作简单的字符串替换,不作任何检查。 在进行宏定义时,可以引用已定义的宏名 无参宏定义的一般格式: #define 标识符字符串 将这个标识符(名字)称为“宏名”,在用预编译时将宏名替换成字符串的过程称为“宏展开”。#define是宏定义命令。 带参宏定义的一般格式: #define 宏名(形参表)字符串 带参宏的调用和宏展开: 调用格式:宏名(实参表); 宏展开(又称为宏替换)的方法:用宏调用提供的实参直接臵换宏定义中相应的形参,非形参字符保持不变。 定义有参宏时,宏名与左圆括号之间不能留有空格。否则,C编译系统会将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。 有参宏的展开,只是将实参作为字符串,简单地臵换形参字符串,而不做任何语法检查。 为了避免出错,可以在所有形参外,甚至整个字符串外,均加上一对圆括号。 如: #define S(r) 3.14*(r)*(r) 则:area=S(a+b); 展开后为: area=3.14*(a+b)*(a+b); 调用有参函数时,是先求出实参的值,然后再复制一份给形参。而展开有参宏时,只是将实参简单地臵换形参。函数调用是在程序运行时处理的,为形参分配临时的内存单元;而宏展开则是在编译前进行的,在展开时不分配内存单元,不进行值的传递,也没有“返回值”的概念。调用函数只可得到一个返回值,而用宏可以设法得到几个结果。 在有参函数中,形参都是有类型的,所以要求实参的类型与其一致;而在有参宏中,形参和宏名都没有类型,只是一个简单的符号代表,因此,宏定义时,字符串可以是任何类型的数据。 使用宏次数多时,宏展开后源程序变长,因为每展开一次都是程序增长,而函数调用不会使源程序变长。 宏替换不占用运行时间,只占编译时间。而函数调用则占用运行时间(分配单元、保留现场、值传递、返回)。 在程序中如果有带实参的宏,则按#define命令行中指定的字符串从左到右进行臵换。如果字符串中包含宏中的形参,则将程序语句中相应的实参(可以是常量、变量或表达式)代替形参。如果宏定义中的字符串中的字符不是参数字符,则保留。

C二级 第9章 编译预处理和动态存储分配

1.以下叙述中正确的是()。 A) 在C语言中,预处理命令行都以"#"开头 B) 预处理命令行必须位于C源程序的起始位置 C) #include 必须放在C程序的开头 D) C语言的预处理不能实现宏定义和条件编译的功能 参考答案:A 【解析】预处理命令是以"#"号开头的命令,它们不是C语言的可执行命令,这些命令应该在函数之外书写,一般在源文件的最前面书写,但不是必须在起始位置书写,所以B),C)错误。C)语言的预处理能够实现宏定义和条件编译等功能,所以D)错误。 2.以下关于宏的叙述中正确的是()。 A) 宏替换没有数据类型限制B) 宏定义必须位于源程序中所有语句之前 C) 宏名必须用大写字母表示D) 宏调用比函数调用耗费时间 参考答案:A 【解析】宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头,所以B)选项中宏定义必须位于源程序中所有语句之前是错误的。宏名一般用大写,但不是必须用大写,所以C)选项错误。宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值),所以D)选项错误。 3.有以下程序: #include #define PT 3.5 ; #define S(x) PT*x*x ; main() {int a=1,b=2;printf("%4.1f\n" ,S(a+b));} 程序运行后的输出结果是()。 A) 7.5 B) 31.5 C) 程序有错无输出结果D) 14.0 参考答案:C 【解析】宏定义不是C语句,末尾不需要有分号。所以语句printf("%4.1f\n" ,S(a+b));展开后为 printf("%4.1f\n" ,3.5;*a+b*a+b;);所以程序会出现语法错误。 4.若程序中有宏定义行: #define N 100 则以下叙述中正确的是 A) 宏定义行中定义了标识符N的值为整数100 B) 在编译程序对C源程序进行预处理时用100替换标识符N C) 上述宏定义行实现将100赋给标示符N D) 在运行时用100替换标识符N 参考答案:B 【解析】本题考查预编译相关知识,宏定义在编译程序时做了一个简单的替换,所以选项B正确。 5.有以下程序 #include #define N 3 void fun( int a[][N], int b[] ) { int i, j; for( i=0; i

第九章编译预处理与带参数的主函数

第九章编译预处理与带参数的主函数 一、单项选择题 1.C程序中,以#号带头的行是预编译(A)。 A.命令 B.语句 C.函数 D.字符串 2.下列正确的预编译命令是(B)。 A.define PI 3.14159 B.#define p(a,b) strcpy(a,b) C. #include stdio.h D. # define PI3.14159 3.下列命令或语句中,正确的是(C)。 A.#define MYNAME= “ABC” B.#include stdio.h C. for(i=0;i<10;i++); D.struct int stu{int name}; 4.下列命令或语句中,正确的是(A)。 A.#define PI 3.14159 B. include “stdio.h” C.for(i=0,i<10,i++)a++ D.static struct {int i;}b={2}; 5.下列命令或语句中,错误的是(B)。 A. #define PI 3.14159 B.#include C.if(2); D.for(; ;) if(1) break; 6.定义带参数的宏计算两式乘积(如x2+3x-5与x-6),下列定义中哪个是正确的(C)。 A.#define muit(u,v)u*v B.#define muit(u,v)u*v; C.#define muit(u,v)(u)*(v) D.#define muit(u,v)=(u)*(v) 7.宏定义#define div(a,b) a/b的引用div(x+5,y-5)替换展开后是(A)。 A.x+5/y-5 B.x+5/y-5; C.(x+5)/(y-5) D.(x+5)/(y-5); 8.定义带参数的宏“#define jh(a,b,t)t=a;a=b;b=t”,使两个参数a、b的值交换,下列表述中哪个是正确的(D)。 A.不定义参数a和b将导致编译错误 B.不定义参数a、b、t将导致编译错误 C.不定义参数t将导致运行错误 D.不必定义参数a、b、t类型 9.设有宏定义#define AREA(a,b) a*b,则正确的“宏调用”是(D)。 A.s=AREA(r*r) B.s=AREA(x*y) C.s=AREA D.s=c*AREA((x+3.5),(y+4.1)) 10.设有以下宏定义,则执行语句z=2*(N+Y(5+1));后,z的值为(C)。 #define N 3 #define Y(n) ((N+1)*n) A.出错 B.42 C.48 D.54 11.设有以下宏定义,int x,m=5, n=1时,执行语句IFABC(m+n,m,x);后,x的值为(B)。 #define IFABC(a,b,c) c=a>b?a:b A.5 B.6 C.11 D.出错 12.以下程序中的for循环执行的次数是(C)。 #include “stdio.h” #define N 2 #define M N+1 # define NUM (M+1)*M/2 void main( ) { int i,n=0;

第九章 预处理

第九章预处理 A部分(本、专科必做) 一、选择题 以下不正确的叙述是(D) A、宏替换不占用运行时间。 B、宏名无类型。 C、宏替换只是字符替换。 D、宏名必须用大写字母表示。 C语言的编译系统对宏命令的处理(D) A、在程序运行时进行的。 B、在程序连接时进行。 C、和C程序中的其它语句同时进行编译的。 D、在对源程序中其它语句正式编译之前进行的。 3、以下程序的输出结果是(C)。 A、15 B、100 C、10 D、150 #define MIN(x,y) (x)<(y)?(x):(y) void main() { int I,j,k; i=10;j=15;k=10*MIN(i,j); printf(“%d\n”,k); } 4、以下叙述中正确的是(D) 用#include包含的文件的后缀必须是“.h”。 若一些源程序中包含某个头文件;当该文件有错时,只需对该头文件进行修改,包含此头文件的所有源程序不必重新进行编译。 宏命令行可以看作是一行C语句。 预处理是在编译之前进行的。 5、以下叙述中正确的是(C) A、源程序中所有的行都参加编译。 B、宏定义常量与const定义常量是一样的。 C、宏定义可以嵌套定义,即在宏定义中的字符串可以引用另一个宏定义的标识符。 D、以上都不正确。 二、填空题 以下程序中for 循环执行的次数是 6 。 #define N 2 #define M N+1 #define NUM (M+1)*M/2 void main() { int i; for(i=1;i<=NUM;i++); printf(“%d\n”,i); } 2、以下程序的输出结果是x=93 。 #define A 3 #define B(a) ((A+1)*a)

第九章改 预处理命令习题答案

第九章习题答案 一、单项选择题 1.A 2.B 3.C 4.D 5.B 6.C 7.A 8.D 9.D 10.C 11.B 12.C 13.D 14.C 二、填充题 1.编译处理编译预处理 2.非静态存储类型变量和外部函数 3.7 4.printf(“%d\n”,m); 5.fopen(“a.txt”,”rw”); 6.x[i]>=’A’&&x[i]<=’Z’ 7.“ i=%d\n” 8.(1) #define MAX(a,b,c) (2) #define MIN(a,b) (a=’0’&& c<=’9’) (4) #define isupper( c) (c>=’A’&& c<=’Z’) (5) #define islower( c) (c>=’a’ && c<=’z’) 三、程序分析题 1.运行结果: -3 2.运行结果: 7,47 3.运行结果:50 25 4.运行结果:x=9, y=5 5.运行结果:9 9 11 6.输出结果: x|y&z=3 x^y&~z=1 x&y&z=0 !x|x=1 ~x|x=-1

四、程序设计题 1.解: #include #include #define S(a, b, c) 0.5* (a+b+c) #define AREA(a, b, c) sqrt (S(a, b, c)*(S(a, b, c)-a)* (S(a, b, c )-b)* (S(a, b, c)-c)) void main ( ) { float a, b, c; printf (“输入三角形的三条边长:a, b, c\n”); scanf (“%f, %f, %f”, &a, &b, &c) ; if (( a+b>c)&& (b+c>a) && (c+a>b)) { printf (“周长=%f\n”, 2*S(a, b, c )); printf (“面积=%f\n”, AREA(a, b, c )); } else printf (“a, b, c 的长度不能构成三角形\n”) ; } 2.解: #include #include void main (int argc, char *argv[ ]) { int a, b; if (argc<3) { printf (“Parameter missing!\n”) ; exit(0); } a=atoi (argv[1]) ; b=atoi (argv[2]) ; //在stdlib.h中有库函数atoi, 把字符串转换成整数 printf (“%5d + %5d = %5d\n”, a, b, a+b) ; printf (“%5d - %5d = %5d\n”, a, b, a-b) ; printf (“%5d * %5d = %5d\n”, a, b, a*b) ; printf (“%5d / %5d = %5d\n”, a, b, a/b) ; printf (“%5d %% %5d = %5d\n”, a, b, a%b) ; } 3.解: #include #include void main (int argc, char *argv[]) { int i, sum=0; for (i=1; i #include #include

第九章编译预处理

第九章 编译预处理 编译指令(编译预处理指令):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 终止宏名的作用域。

第九章 预处理命令

第九章 预处理命令 一、选择题 1.以下叙述不正确的是 。 A)预处理命令行都必须以#开始 B)在程序中凡是以#开始的语句行都是预处理命令行 C)C程序在执行过程中对预处理命令行进行处理 D)预处理命令行可以出现在C程序中任意一行上 2.以下叙述中正确的是 。 A)在程序的一行上可以出现多个有效的预处理命令行 B)使用带参数的宏时,参数的类型应与宏定义时的一致 C)宏替换不占用运行时间,只占用编译时间 D)C语言的编译预处理就是对源程序进行初步的语法检查 3.以下有关宏替换的叙述不正确的是 。 A)宏替换不占用运行时间B)宏名无类型 C)宏替换只是字符替换D)宏名必须用大写字母表示 4.在“文件包含”预处理命令形式中,当#include后面的文件名用””(双引号)括起时,寻找被包含文件的方式是 。 A)直接按系统设定的标准方式搜索目录 B)先在源程序所在目录中搜索,再按系统设定的标准方式搜索 C)仅仅搜索源程序所在目录 D)仅仅搜索当前目录 5.在“文件包含”预处理命令形式中,当#include后名的文件名用<>(尖括号)括起时,寻找被包含文件的方式是 。 A)直接按系统设定的标准方式搜索目录 B)先在源程序所在目录中搜索,再按系统设定的标准方式搜索 C)仅仅搜索源程序所在目录 D)仅仅搜索当前目录 6.在宏定义#define PI 3.1415926中,用宏名PI代替一个 。 A)单精度数B)双精度数C)常量D)字符串 7.以下程序的运行结果是 。 #include #define ADD(x) x+x void main ( ) { int m=1,n=2,k=3,sum ; sum = ADD(m+n)*k ; printf(“%d\n”,sum) ; A)9 B)10 C)12 D)18 8.以下程序的运行结果是 。 #include #define MIN(x,y) (x)>(y) ? (x) : (y) void main ( ) { int i=10, j=15 , k; k = 10*MIN(i,j); printf(“%d\n”,k);

第九章预处理的考试试题

(1) 下面叙述错误的是()。 A) “#define PRICE 30”命令的作用是定义一个与30等价的符号常量PRICE B) C源程序中加入一些预处理命令是为了改进程序设计环境,提高编程效率 C) “#include ”命令的作用是在预处理时将stdio.h文件中的实际内容代替该命令 D) 宏定义也是C语句,必须在行末加分号 (2) 若有定义:#define PI 3,则表达式PI*2*2的值为()。 A) 4 B) 不确定 C) 12 D) 322 (3) 以下程序的运行结果是()。 #define X a+b main( ) { int a=3,b=4,s1; s1=2*X; printf("%d\n",s1); } A) 8 B) 14 C) 10 D) 6 (4) 若有定义#define F 2+3,则表达式F*F的值为()。 A) 13 B) 17 C) 25 D) 11 (5) 下面叙述正确的是()。 A) 宏名必须用大写字母表示 B) 一个源程序只能有一个预编译处理命令行 C) 宏展开不占用运行时间,只占用编译时间 D) 预处理命令也是C语句,必须以分号结束 (6) 下列程序中定义的二维数组a的()。 # define M 3 # define N 4 void main( ) { int a[M][N]; …… }

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 #define TRUE 1

相关文档
最新文档