第六讲编译预处理
第六讲:第六章编译预处理

第六章编译预处理C语言的一个重要特征是它有编译预处理,一个高级语言源程序要在计算机上运行,必须先用编译预程序将其翻译成为机器语言,编译包括词法分析,语法分析,代码生成,代码优先等步骤,有时在编译之前还要做些预处理功能。
如去掉格式,变换格式等。
C 语言允许在源程序中包含预处理命令,在正式编译之前系统先对这些命令“预处理”,然后整个源程序在进行通常的编译处理。
C语言提供的预处理功能主要有3种:文件包含宏定义条件编译一.文件包含:一般形式:#include “文件名”如:#include”stdio.h”#include”math.h”等注1.一个#include命令只能指定一个被包含文件,如果要包含多个文件,则要用多个#include命令。
注2.在#include命令中,文件名可以用双引号或尖括号括起来如:#include“stdio.h”#include <stdio.h> 二.宏定义:宏定义有两种形式:一种是不带参数的宏定义,另一种是带参数的宏定义。
1.不带参数的宏定义:一般形式:#define 宏名字符串作用:用一个指定的宏名来代表一个常量。
宏名通常用大写。
例:#include“stdio.h”#define M 3#define N M+1#define S N*N/2main( ){printf(“S=%d\n”,S);printf(“5*S=%d\n”,5*S);}2.带参数的宏定义:一般形式:#define 宏名(参数表) 字符串例:#include”stdio.h”#define MAX(x,y) (x>y?x:y)main( ){int a,b;scanf(“%d%d”,&a,&b);printf(“MAX is %d\n”,MAX(a,b)); }。
C语言程序设计课件:编译预处理

编译预处理
9.1 宏定义 9.2 文件包含 单元小结
编译预处理
9.1 宏 定 义
9.1.1 不带参数的宏定义 不带参数的宏定义的一般形式如下: # define 标识符 字符串 其作用是用一个指定的标识符来代表一个字符串。
说明:其中的“#”表示这是一条预处理命令;“define” 为宏定义命令;“标识符”为所定义的宏名;“字符串”可 以是常数、表达式、字符串等。例如:
926*r*r*r;
printf("r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2f\n",r,l,s,v);
}
编译预处理 图9-4 例9-4程序运行结果
编译预处理 实参r的值已知,可以从宏返回三个值:l、s、v。其实,
这只不过是字符置换而己,即将字符r置换R,l置换L,s置 换S,v置换V,而并未在宏展开时求出l、s、v的值。
# define MAX(x,y) (x)>(y)?(x):(y)
main()
{
int a,b,c,d,t;
…
t=MAX(a+b,c+d);
…
}
编译预处理 赋值语句展开后如下:
t=(a+b)>(c+d)?(a+b):(c+d);
MAX不是函数,程序中只有一个main函数,在main函 数中就能求出t的值。这个问题也可用函数的形式表示:
# define PI 3.14159 上述命令的作用是用标识符PI代替常量3.14159。在编 译预处理时,将程序中该定义以后的所有标识符PI都用 3.14159代替。
编译预处理
【例9-1】 输入圆的半径,求圆的周长、面积和球的体
C语言编译预处理PPT课件

知识点精讲
知识点2 不带参数的宏定义
知识点分析 用一个指定的标识符(即名字)来代表一个字符串,其一般形式如下: #define 标识符 字符串 例如: #define PI 3.1415926 1.宏定义的作用是在本程序文件中用指定的标识符PI来代替3.141 592 6这个字符串。在编译 预处理时,将程序中在该命令后出现的所有PI都用3.141 592 6代替。这种方法使用户能够以一个 简单的名字代替一个长的字符串。 2.这个标识符(名字)称为“宏名”。 3.在预处理时,将宏名替换成字符串的过程称为“宏展开”。“#define”是宏定义命令。 例如: #include "stdio.h" #define PI 3.1415926 /*定义了一个不带参数的宏*/
知识点精讲
int sum=ADD ( m+n)*k; printf("sum=%d", sum); } 2.下列程序的运行结果是____s_u_m_=_1_2_______ #define ADD(x)(x)+(x) void main( ) { int m=1,n=2,k=3; int sum=ADD(m+n)*k; printf("sum=%d", sum); } 3.下列程序的运行结果是______2_8________ #include <stdio.h> #define f(x) x*x main( )
void main( )
{
printf("L=%f\nS=%f\n",L,S);
getch( );
}
知识点精讲
知识点3 带参数的宏定义
真题回顾 (2021年真题)计算圆的面积s(公式为s=3.14r2)。请补全下列代码。 #include<stdio.h>
第6章 编译预处理

对源程序的影响 无影响。 时间占用 占用程序运行时间。
表6-1
☆☆ 第6章 编译预处理
带参数的宏和函数
14
6.1.2 有参宏定义
(4)宏定义中,若替换字符串中的形式参数在引号中,则宏展 开时不被实参替换。但如果在替换字符串中,形式参数以#作为 前缀,那么宏展开时它将被带引号的实参字符串替换,如: #define MYPRINT1(exp) printf("exp=%d\n",exp) #define MYPRINT2(exp) printf(#exp"=%d\n",exp) void main() { MYPRINT1(2+3);/*宏展开 print("exp=%d\n",2+3); */ MYPRINT2(2+3);/*宏展开 print("2+3" "=%d\n",2+3); */ /*其中字符串被连接起来,即print("2+3=%d\n",2+3); */ } 运行结果如下: exp=5 2+3=5 ☆☆ 第6章 编译预处理 15
第6章 编译预处理 本章要求:
掌握无参数宏和带有参数宏定义和使用方法; 掌握文件包含的使用方法; 掌握条件编译的使用。
本章重点:
无参数宏和带有参数宏定义和使用方法
本章难点:
带有参数宏定义和使用方法
☆☆ 第6章 编译预处理 1
第6章 编译预处理
所谓编译预处理,指的是在源程序文件中,加入“编 译预处理命令”,使编译程序在对源程序进行通常的编译 之前,先对这些特殊的命令进行“预处理”,然后将预处 理的结果和源程序一起再进行通常的编译处理,以得到目 标代码(OBJ文件)。
编译预处理ppt课件

第6章 在日常生活中,随处都可以看到浪费粮食的现象。也许你并未意识到自己在浪费,也许你认为浪费这一点点算不了什么
编译预处理
【例6.1】求三角形的周长、面积和体积。
#define PI 3.14159 main( ) { float l,s,r,v; printf("input redius:\n" ); scanf("%f",&r);
#define D "%d"
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"
第6章 在日常生活中,随处都可以看到浪费粮食的现象。也许你并未意识到自己在浪费,也许你认为浪费这一点点算不了什么
第6章 在日常生活中,随处都可以看到浪费粮食的现象。也许你并未意识到自己在浪费,也许你认为浪费这一点点算不了什么
编译预处理
【例6.2】嵌套宏定义。
#define R 3.0 #define PI 3.14159 #define L 2*PI*R
运行结果为: L=18.849540 S=28.274310
编译预处理
(5) 宏定义也有定义域,它的定义域是从开始定义处到本程序 文件的结尾。所以一般都将宏定义放在源程序开头。如果终 止使用宏,可以使用编译预处理命令“#undef”来终止宏的 定义域,即宏的定义域应该是从定义处到文件尾或命令“# undef”出现处。
#define PI 3.14159
/ * 定义宏PI为 3.14159 */
┆
s=PI *r *r;
第6章 编译预处理

1. #define SWAP(x,y) t=x;x=y;y=t main() {int a,b,t; scanf("%d,%d",&a,&b); SWAP(a,b); printf("a=%d,b=%d",a,b,b) ((a)>(b)?(a):(b)) main() {int x,y,z; scanf("%d,%d,%d",&x,&y,&z); printf("max is:%d",MAX(MAX(x,y),z)); }
6.1 宏定义
在C语言中,"宏"分为无参数的宏(简称无参宏)和有参数 的宏(简称有参宏)两种. 1. 无参宏定义
无参宏定义的一般格式: 无参宏定义的一般格式: #define 标识符 字符串 作用:用指定的标识符来代表一个字符串,只是做符号替换. 其中:"define"为宏定义命令;"标识符"为所定义的宏名,通常用大写 字母表示,以便于与变量区别;"字符串"可以是常数,表达式,格式 串等. 如:定义符号常量 #define PI 3.14159 注: 宏定义命令#define出现在函数的外部,一般在开头部分,宏名的有效范 围是:从定义命令之后, 到本文件结束.若要终止宏定义的作用域用 #undef命令. 在进行宏定义时,可以引用已定义的宏名 ,进行层层置换. 见书P94例6.2
第6章 编译预处理
所谓编译预处理是指,在对源程序进行编译之前,先对源程 序中的编译预处理命令进行处理;然后再将处理的结果,和源 程序一起进行编译,以得到目标代码. 预处理命令不是实现程序的功能,而是向编译系统发布信息 和命令.有三种: 6.1 宏定义 6.2 文件包含 6.3 条件编译 以'#'开头,每个命令单独占一行,结束无符号
编译预处理

………..
area=S(3,2);
宏展开:
area=3*2;
宏展开:形参用 实参换,其它字符 保留 宏体及各形参外 一般应加括号()
例 #define POWER(x) x*x
x=4; y=6;
z=POWER(x+y); 宏展开:z=x+y*x+y; 一般写成: #define POWER(x) 宏展开: z=((x+y)*(x+y));
形式二: #ifndef 标识符
程序段1 [#else
程序段2] #endif
形式三: #if 表达式
程序段1 [#else
程序段2] #endif
1. 预处理命令是一种特殊命令,为了区别一般C语句, 必须以#开头,结尾不加分号。
2. 预处理命令可以放在程序中的任何位置,其有效范围 是从定义开始到文件结束。
3. 宏定义分为不带参数的宏定义和带参数的宏定义两种
4. 条件编译的三种形式。
整理课堂笔记 完成课后习题
文件包含(2/3)
#include “file2.c”
A
file1.c
被包含文件内容 源文件(*.c) 头文件(*.h)
B file2.c
宏定义 数据结构定义 函数说明等
file2.c A file1.c
文件包含(3/3)
文件包含可嵌套
#include “file2.c” #include “file3.c”
编译预处理包含:
宏定义 文件包含 条件编译
不带参数的宏定义 宏体可缺省,表示宏名 定义过或取消宏体
一般形式: #define 宏名 [宏体] 功能:用指定标识符(宏名)代替字符序列(宏体)
第06章 编译预处理

6.2.1 不带参数的宏
格式: 格式: #define 宏名 字符串 其中,宏名为标识符。 其中,宏名为标识符。 举例: 举例: #define PI 3.1415f 其作用是将宏名PI定义为字符串3.1415 PI定义为字符串3.1415f 其作用是将宏名PI定义为字符串3.1415f。在编译预 处理时,将该命令后所有出现PI处均用3.1415 替换。 PI处均用3.1415f 处理时,将该命令后所有出现PI处均用3.1415f替换。 这种替换称为“宏代换” 宏扩展” 宏展开” 这种替换称为“宏代换”或“宏扩展”或“宏展开”。 通常宏名用大写字母,以区别于变量名。但语法上并 通常宏名用大写字母,以区别于变量名。 没有这样的要求。 没有这样的要求。
6.2 宏
宏应先定义后使用。 宏应先定义后使用。 宏定义:用预处理命令#define实现。 宏定义:用预处理命令#define实现 实现。 宏定义分为: 宏定义分为: –带参数的宏定义; 带参数的宏定义; 带参数的宏定义 –不带参数的宏定义。 不带参数的宏定义。 不带参数的宏定义 宏的优点:见名知意,简化书写,方便修 宏的优点:见名知意,简化书写, 改。
6.2.2 带参数的宏
带参数宏定义的形式: 带参数宏定义的形式: 宏名(参数表) #define 宏名(参数表) 字符串 其中,参数表的参数之间用逗号分隔, 其中,参数表的参数之间用逗号分隔,参数仅用标识 符表示,不能指定参数类型。 符表示,不能指定参数类型。 举例: 举例: 宏定义中的参数称为形参。 宏定义中的参数称为形参。 #define PI 3.1415f 例如:宏CIRCUM有参数r。 例如: CIRCUM有参数 有参数r #define CIRCUM(r) 2*PI*r 带参宏扩展:先用实参替代宏 带参宏扩展: float b=CIRCUM(3.5); 定义中的形参, 定义中的形参,并用替代后的
计算机本科C语言讲稿-编译预处理

6.2.2
带参宏定义
C语言允许宏带有参数。在宏定义中的参数称为形 式参数,在宏调用中的参数称为实际参数。 对带参数的宏,在调用中,不仅要宏展开,而且要 用实参去代换形参。 带参宏定义的一般形式为: #define 宏名(形参表) 字符串 带参宏调用的一般形式为: 宏名(实参表);
例如: #define M(y) y*y+3*y …… k=M(5); ……
对文件包含命令还要说明以下几点: 1. 包含命令中的文件名可以用双引号括起 来,也可以用尖括号括起来。例如以下 写法都是允许的: #include "stdio.h" #include <math.h> 但是这两种形式是有区别的:使用尖括 号表示在包含文件目录中去查找(包含目 录是由用户在设置环境时设置的),而不 在源文件目录去查找;
4. 在宏定义中,字符串内的形参通常要用括号括 起来以避免出错。 【例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
");
经预编译后:
main(){ int x,y,max; printf("input two numbers: scanf("%d%d",&x,&y); max=(x>y)?x:y; printf("max=%d\n",max); }
");Biblioteka 于带参的宏定义有以下问题需要说明:
C语言程序设计之程序编译预处理介绍课件

演讲人
01.
02.
03.
04.
目录
编译预处理的概念
编译预处理的指令
编译预处理的应用
编译预处理的注意事项
编译预处理的概念
编译预处理的作用
提高程序的可读性和可维护性
提高程序的编译效率
提高程序的可移植性
提高程序的安全性和健理指令,如#include、#define等
预定义宏:使用预定义宏可以方便地获取系统信息,提高程序的可移植性和适应性。
04
文件包含:使用文件包含可以方便地组织代码,提高程序的模块化和可重用性。
03
条件编译:使用条件编译可以控制代码的编译和运行,提高程序的灵活性和可移植性。
02
宏定义:使用宏定义可以简化代码,提高程序的可读性和可维护性。
01
模块化编程
优点:便于修改和维护代码
缺点:增加了编译时间
缺点:可能导致代码可读性降低
编译预处理的指令
宏定义指令
#define指令:用于定义宏,在编译时进行替换
#undef指令:用于取消宏定义
#ifdef指令:用于判断宏是否定义
#ifndef指令:用于判断宏是否未定义
#else指令:用于定义未定义宏时的处理
#endif指令:用于结束条件编译块
#error指令:用于在编译时输出错误信息
#pragma指令:用于向编译器传递一些信息,如警告信息等
#line指令:用于指定下一行代码的行号
#include指令:用于包含其他头文件
#ifdef:如果定义了指定的宏,则编译后面的代码#ifndef:如果没有定义指定的宏,则编译后面的代码#else:与#ifdef或#ifndef配合使用,表示如果没有满足条件,则编译后面的代码#endif:结束条件编译指令#undef:取消已定义的宏#error:当满足条件时,输出错误信息并停止编译#line:指定下一行代码的行号和文件名#pragma:用于向编译器传递一些特定的信息,如警告信息等
课件:编译预处理

例7.2 输入三个值,写一个宏判断以这三个值作
边长,能否构成三角形。
#include <stdio.h>
#define f(a,b,c) ( (a)+(b)>(c) && (a)+(c)>(b) && \
#define展N开N后N*N/2
main()printf(“NN=%d\n”,3+1*3+1/2);
{
printf(“5*NN=%d\n”,5* 3+1*3+1/2);
printf("NN=%d\n",NN);
printf("5*NN=%d\n",5*NN);
}
运行结果:
NN=6
5*NN=18
7.1.2 带参数宏定义
– 一般形式: #include “文件名”
或 #include <文件名>
处理过程:预编译时,用被包含文件的内容取代该预处理命 令,再对“包含”后的文件作一个源文件编译
常用包含文件
ctyp#ein.hcl-u-de字“f符ile2处.h”理函数
math.h-- 数学file函2.h数
stdio.h-- 标准输入/输出函B数 stdlib.h-- 常用函数库
printf("yes\n");
else printf("no\n");
return 0;
}
例7.3 写出下列程序的输出结果。
第06讲 编译预处理命令

PS:非值传递,而是传递形参字符
条件编译
#if (常量表达式) =====> 注意常量,非变量
仅当表达式为真,则编译它与#endif之间的代码
#if (status == debug) printf("程序调试中\n"); #elif (status == beta) printf("程序测试中\n"); #else printf("欢迎使用正式版!\n"); #endif
条件编译
#ifdef #ifndef 如果有定义 如果没定义 (if define) (if not define)
#ifndef PI define PI 3.14 #endif
预处理指令
#include #define 引用各种函数库 定义常用的宏
条件编译
根据具体情况来决定要编译的内容: #if、#else、#elif、#endif #ifdef、#ifndef 视具体情况来决定要编译和定义的内容
(if define) (if not define)
#include
1)两种形式: #include <stdio.h> #include "stdio.h" 到配置目录中找 从当前目录开始找,无则到配置目录中找
2)文件包含允许嵌套 文件A包含了文件B,文件B又包含了文件C,最终文件B和C都会被引 入A文件
魔舟工作室
C语言入门
第六讲 编译预处理命令
Lellansin@
预处理指令
#include #define 包含 宏定义
#if #else #elif #endif
如果,则预定义 否则,预定义 否则如果,则预定义 结束条件判断
编译预处理

【范例6-6】 #define SQ(y) (y)*(y) //宏定义,形参是变量y #include <iostream.h> void main() { int a,sq; cout<<"input a number: "; cin>>a; sq=SQ(a+1); //宏调用,实参是表达式a+1 } cout<<"sq="<<sq<<endl; 注:在宏定义中的形式参数是变量或者是变量,而宏 调用中的实参则不必一定是变量或常量,也可以是 表达式。
6.3.3 条件编译命令
(3)#if 常量表达式1 程序段1 #elif常量表达式2 程序段2 …… #elif常量表达式n 程序段n #else 程序段n+1 #endif 该条件编译命令的功能是:依次计算常量表达式的值,当表达式的值 为真时,则用相应的程序段参与编译,如果全部表达式的值都为假, 则用else后的程序段参与编译。
6.2 宏
概念: 宏(macro)是C++提供的一种预处理命令,也是使用较为 广泛的一个概念。 简单来说,宏是一种以相同的源语言执行预定义指令序列的指 令。在C++中,通过宏的使用,可以将一个表达式定义成宏, 并在C++的源程序中随意调用。 宏定义命令有两种格式:一种是简单(不带)的宏定义,另一 种是带参数的宏定义。
#define MAX(a,b) (a>b)?a:b #include <iostream.h> void main() { int x,y,max; cout<<"input two numbers: "; cin>>x>>y; max=MAX(x,y);//调用宏MAX,相当于 max=(x>y)?x:y; cout<<"max="<<max<<endl; }
C++程序设计第6章编译预处理

编译预处理的基本流程
识别预处理指令
编译器识别源代码中的预处理指 令,如#include、#define等。
执行预处理指令
根据识别的预处理指令,编译器 执行相应的操作,如包含头文件、 定义宏等。
生成预处理后的代码文件
编译器将预处理后的代码写入一 个临时文件中,这个文件称为预 处理后的代码文件。
读取源代码文件
宏定义与宏替换的注意事项
总结词:在使用宏定义与宏替换时,需要注 意一些关键点以避免潜在的问题。
01
1. 宏定义中的值通常不使用复杂的表达式或 语句,以避免预处理器处理时的混淆。
03
02
详细描述
04
2. 使用宏替换时要特别注意括号的使用, 以避免优先级错误或意外的替换。
3. 尽量避免使用带有副作用的宏,因为 它们可能导致代码难以理解和调试。
编译器首先读取源代码文件。
编译预处理后的代码文件
编译器开始编译预处理后的代码 文件,生成目标文件或可执行文 件。
预处理指令
02
define指令
壹
定义符号常量
使用#define指令可以定义符号常量,例如#define PI 3.14159。在程序中,可以使用PI代替3.14159。
贰
定义宏函数
除了定义符号常量,#define还可以定义宏函数,例如#define SQUARE(x) ((x) * (x))。在程序中,使用SQUARE(5)代替((5) * (5))。
C++程序设计第6章编译预处理
单击此处添加副标题
汇报人姓名 汇报日期
目 录CONTENTS
1 编译预处理简介 2 预处理指令 3 宏定义与宏替换 4 条件编译 5 头文件的保护与重复
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第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<stdio.h>#define M (y*y+3*y)void 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;显然与原题意要求不符。
计算结果当然是错误的。
【例】#include<stdio.h>#define OK 100void main(){printf("OK");printf("\n");}运行程序输出:OK程序的运行结果为:OK这表示把“OK”当字符串处理。
6.2.2 带参宏定义C语言允许宏带有参数。
在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。
◇对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:#define 宏名(形参表) 字符串在字符串中含有各个形参。
带参宏调用的一般形式为:宏名(实参表);例如:#define M(y) y*y+3*y /*宏定义*/……k=M(5); /*宏调用*/……在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:k=5*5+3*5【例】#include<stdio.h>#define MAX(a,b) (a>b)?a:bvoid 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中的大数。
带参宏定义中,宏名和形参表之间不能有空格出现。
例如把:#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)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
【例】#include<stdio.h>#define SQ(y) (y)*(y)void main(){int a,sq;printf("input a number: ");scanf("%d",&a);sq=SQ(a+1);printf("sq=%d\n",sq);}宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换形参y,再用(y)*(y) 代换SQ,得到如下语句:sq=(a+1)*(a+1);4)定义宏时,宏串中把形参用圆括号括起来和不括是不同的,若宏定义改为:#define SQ(y) y*y则宏替换后为:sq=a+1*a+1;运行结果将完全不同。
【例】#include<stdio.h>#define SQ(y) (y)*(y)void 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:3sq=160分析宏调用语句,在宏代换之后变为:sq=160/(a+1)*(a+1);a为3时,由于“/”和“*”运算符优先级和结合性相同,则先作160/(3+1)得40,再作40*(3+1)最后得160。
为了得到正确答案应在宏定义中的整个字符串外加括号,程序修改如下:【例】#include<stdio.h>#define SQ(y) ((y)*(y))void main(){int a,sq;printf("input a number:");scanf("%d",&a);sq=160/SQ(a+1);printf("sq=%d\n",sq);}对于宏定义不仅应在参数两侧加括号,也应在整个字符串外加括号。
5)带参的宏和带参函数很相似,但有本质上的不同。
【例1】#include<stdio.h>int SQ(int y){return((y)*(y));}void main(){int i=1;while(i<=5)printf("%d:%d\n",i,SQ(i++));}程序运行后输出:2:13:44:95:166:25【例2】##include<stdio.h>#define SQ(y) ((y)*(y))void main(){int i=1;while(i<=5)printf("%d:%d\n",i,SQ(i++));}程序运行输出:1:13:95:25在例1中,形参为Y,函数体表达式为((y)*(y))。
在例2中。
形参也为y,字符串表达式为((y)*(y))。
例1的函数调用为SQ(i++)例2的宏调用为SQ(i++),实参也是相同的。
从输出结果来看,却大不相同。
分析如下:在例1中,函数调用是把实参i值传给形参y后自增1。
然后输出函数值。
因而要循环5次。
输出1~5的平方值。
在例2中宏调用时,只作代换。
SQ(i++)被代换为((i++)*(i++))。
每一次循环时,i完成乘运行后两次自增1,循环三次后,i=7,停止循环。
从以上分析可以看出函数调用和宏调用二者在形式上相似,在本质上是完全不同的。
6)宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。
看下面的例子。
【例】#include<stdio.h>#define SSSV(s1,s2,s3,v) s1=l*w;s2=l*h;s3=w*h;v=w*l*h; void 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);}6.3 文件包含文件包含是C预处理程序的另一个重要功能。
文件包含命令行的一般形式为:#include "文件名"在前面我们已多次用此命令包含过库函数的头文件。
例如:#include"stdio.h"#include"math.h"功能:是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。
应用:一个大的程序可以分为多个模块,由多个程序员分别编程。
有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用。
这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。
号括起来。
例如以下写法都是允许的:#include"stdio.h"#include<math.h>但是这两种形式是有区别的:使用尖括号表示在包含文件目录(include)中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。
2)一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。
3)文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。
6.4 条件编译本章考点⏹宏定义。
⏹文件包含。
典型试题详解1.有如下程序:#define N 2#define M N+1#define NUM 2*M+1main()int i;for(i=1;i<=NUM;i++)printf("%d\n",i);}该程序中的for循环执行的次数是________。