76
1 第7 第第第第第第第第 第第第第第第第第 第第第第第第 第第第第 第第第第第第 第第第第 C C 第第第第第第 第第第第第第

第 7 章 函数与预处理命令

  • Upload
    phuong

  • View
    104

  • Download
    4

Embed Size (px)

DESCRIPTION

C 语言程序设计. 第 7 章 函数与预处理命令. 北京科技大学 计算机系. 第 7 章 函数与预处理命令. 7.1 概述 7.2 函数的定义与调用 7.3 数组作函数参数 7.4 函数的嵌套调用和递归调用 7.5 局部变量和全局变量及其作用域 7.6 变量的存储类别及变量的生存期 7.7 函数的存储分类 7.8 编译预处理 7.9 多文件程序的调试方法. 7.1 概述. C 程序. … …. 源程序文件 1. 源程序文件 n. 函数 1. 函数 n. …. …. 函数 1. 函数 m. ① 程序结构清晰,可读性好。 - PowerPoint PPT Presentation

Citation preview

Page 1: 第 7 章  函数与预处理命令

1

第第 77 章 函数与预处理命令章 函数与预处理命令

北京科技大学 计算机系北京科技大学 计算机系

CC 语言程序设计语言程序设计

Page 2: 第 7 章  函数与预处理命令

2005 3年 月

2第第 77 章 函数与预处理命令章 函数与预处理命令

7.1 7.1 概述概述7.2 7.2 函数的定义与调用函数的定义与调用7.3 7.3 数组作函数参数数组作函数参数7.4 7.4 函数的嵌套调用和递归调用函数的嵌套调用和递归调用7.5 7.5 局部变量和全局变量及其作用域局部变量和全局变量及其作用域7.6 7.6 变量的存储类别及变量的生存期变量的存储类别及变量的生存期7.7 7.7 函数的存储分类函数的存储分类7.8 7.8 编译预处理编译预处理7.9 7.9 多文件程序的调试方法多文件程序的调试方法

Page 3: 第 7 章  函数与预处理命令

2005 3年 月

37.1 7.1 概述概述

① ① 程序结构清晰,可读性好。程序结构清晰,可读性好。② ② 减少重复编码的工作量。减少重复编码的工作量。③ ③ 可多人共同编制一个大程序,缩短程序设可多人共同编制一个大程序,缩短程序设

计周期,提高程序设计和调试的效率。计周期,提高程序设计和调试的效率。

使用使用函数函数

的的好处好处

… …

C 程序

源程序文件 n

函数 1 函数 m

… …源程序文件1

函数 1 函数 n

Page 4: 第 7 章  函数与预处理命令

2005 3年 月

4【【例例 7.17.1 】】求一个整数的立方。求一个整数的立方。int cube (int x)int cube (int x) /* /* 函数定义 函数定义 **//

{ return (x * x * x); }{ return (x * x * x); }

main( )main( )

{ int f, a;{ int f, a;

printf("\nEnter an integer numberprintf("\nEnter an integer number :: ");");

scanf("%d", &a);scanf("%d", &a);

f = f = cube (a);cube (a);

printf("%d * %d * %d = %d\n", a, a, a, f);printf("%d * %d * %d = %d\n", a, a, a, f);

}} 程序运行情况如下:Enter an integer number :22 * 2 * 2 = 8

函数调用函数调用

程序的执行总是从 main 函数开始

Page 5: 第 7 章  函数与预处理命令

2005 3年 月

5

⑴ 一个 C 源程序可以由一个或多个源程序文件组成。 C 编译系统在对 C 源程序进行编译时是以文件为单位进行的。

⑵ 一个 C 源程序文件可以由一个或多个函数组成。所有函数都是独立的。主函数可以调用其它函数,其它函数可以相互调用。

⑶ 在一个 C 程序中,有且仅有一个主函数 main 。C 程序的执行总是从 main 函数开始,调用其它函数后最终回到 main 函数,在 main 函数中结束整个程序的运行。

说明说明

Page 6: 第 7 章  函数与预处理命令

2005 3年 月

6⑷ ⑷ 函数的种类函数的种类从函数定义形式分:① 有参函数:在主调(用)函数和被调(用)函数之间通过参数进行数据传递, 如: int cube (int x) { … } ② 无参函数:如: getchar( )在调用无参函数时,主调函数不需要将数据传递给无参函数。

从使用的角度看:从使用的角度看:① ① 标准函数(库函数)标准函数(库函数)库函数是由系统提供的。库函数是由系统提供的。如:如: getchar( )getchar( ) 、、 sin(x)sin(x)等。等。在程序中可以直接调在程序中可以直接调用它们。附录用它们。附录 AA 列出了列出了CC 的部分库函数。的部分库函数。② ② 用户自定义函数。用户自定义函数。如如 :: 例例 7.17.1 中的中的 cubecube 函数。函数。

Page 7: 第 7 章  函数与预处理命令

2005 3年 月

7【【例例 7.27.2 】】无参函数的定义与调用。无参函数的定义与调用。void welcome ( ){ printf("*********************************\n"); printf(" Welcome to China \n"); printf("*********************************\n");}main( ){ welcome( );}

程序的输出结果如下:********************************* Welcome to China*********************************

Page 8: 第 7 章  函数与预处理命令

2005 3年 月

87.2.1 7.2.1 函数的定义函数的定义函数定义的一般形式函数定义的一般形式

函数类型 函数名 ( 类型名 形式参数 1 ,… ){ 说明语句 执行语句}

例如:求两个数的最大值。 int max(int x,int y){ int z; z = x > y ? x : y; return( z );}

类型省略时默认为 int类型 没有形式参数

为无参函数

Page 9: 第 7 章  函数与预处理命令

2005 3年 月

9

int max(x,y)

int x,y;

{ int z;

z = x > y ? x : y;

return( z );

}

int max(x,y){ int x,y;……}或int max(int x,y){ …… }

或int max(x,y)int x,y,z;{ z = x > y ? x : y; return( z );}

花括号中也可以为空,这种函数叫花括号中也可以为空,这种函数叫空函数空函数 。 。不能在函数体内定义其他函数,即函数不能在函数体内定义其他函数,即函数不能嵌套定义不能嵌套定义。。

形参也可以这样定义 如下定义都是错误的如下定义都是错误的

Page 10: 第 7 章  函数与预处理命令

2005 3年 月

10

函数名(实参表列)

在 C 语言中,把函数调用也作为一个表达式。因此凡是表达式可以出现的地方都可以出现函数调用。例如:

① welcome( ); ② if (iabs (a)>max) max=iabs(a); ③ m=max(c,max(a,b));

7.2.2 7.2.2 函数的调用函数的调用函数调用的一般形式:

Page 11: 第 7 章  函数与预处理命令

2005 3年 月

11

int sum100( )int sum100( ){ int i,t=0;{ int i,t=0; for (i=1; i<=100; i++)for (i=1; i<=100; i++) t+=i;t+=i; return (t);}return (t);}main( )main( ){ int s;{ int s; s=sum100( );s=sum100( ); printf("%d\n", s);printf("%d\n", s);}}

程序输出结果:5050

int sum ( int x )int sum ( int x ){ int i,t=0;{ int i,t=0; for (i=1; i<=x; i++)for (i=1; i<=x; i++) t+=i;t+=i; return (t);return (t);}}main( )main( ){ int s;{ int s; s=sum (100);s=sum (100); printf("%d\n", s); printf("%d\n", s); }}

【【例例 7.37.3 】】求求 11 ~~ 100100 的累加和。的累加和。

思考:两个程序有何不同

程序输出结果:5050

Page 12: 第 7 章  函数与预处理命令

2005 3年 月

12

void swap(int x, int y) { int z; z=x; x=y; y=z; printf("\nx=%d,y=%d",x ,y);}main( ){ int a= 10,b=20; swap(a,b); printf("\na=%d,b=%d\n",a,b);}

7.2.3 7.2.3 函数参数与函数的返回函数参数与函数的返回值值

11 .函数的形式参数与实际参数.函数的形式参数与实际参数

程序输出结果:x=20,y=10a=10,b=20

形式参数(形参)

实际参数(实参)实际参数(实参)

【例 7.4 】编一程序,将主函数中的两个变量的值传递给 swap 函数中的两个形参,交换两个形参的值。

单向值传递

Page 13: 第 7 章  函数与预处理命令

2005 3年 月

13有关形参和实参的说明:有关形参和实参的说明:

① 当函数被调用时才给形参分配内存单元。调用结束,所占内存被释放。

② 实参可以是常量、变量或表达式,但要求它们有确定的值。

③ 实参与形参类型要一致,字符型与整型可以兼容。④ 实参与形参的个数必须相等。在函数调用时,实

参的值赋给与之相对应的形参。“单向值传递”。

注意:在注意:在 TCTC 中,实参的求值顺序是从右到左。 中,实参的求值顺序是从右到左。

Page 14: 第 7 章  函数与预处理命令

2005 3年 月

14【【例例 7.57.5 】】函数调用中实参的求值顺序。函数调用中实参的求值顺序。

void fun(int a,int b)void fun(int a,int b)

{ printf("a=%d,b=%d\n",a,b); }{ printf("a=%d,b=%d\n",a,b); }

main( ) main( )

{ int m=5;{ int m=5;

fun(fun(3+m3+m, , m++m++););

}}程序输出结果:a=9,b=5

Page 15: 第 7 章  函数与预处理命令

2005 3年 月

152.2. 函数的类型与函数的返回值函数的类型与函数的返回值

说明:① 函数的类型决定了函数返回值的类型。若省略函数的类型,系统默认其为 int 型。② 无返回值的函数应将其类型定义为 void (空)类型。(空)类型。

⑴ ⑴ 函数的类型函数的类型 【【例例 7.67.6 】】输出两个数中的大数。输出两个数中的大数。max(int x,int y)max(int x,int y){ int z;{ int z; z=x>y?x:y;z=x>y?x:y; return (z); /* return (z); /* 返回返回 zz 的值 的值 **//}}main( )main( ){ int a,b,c;{ int a,b,c; scanf("%d,%d",&a,&b);scanf("%d,%d",&a,&b); c=max(a,b); c=max(a,b); printf("max is %d\n",c);printf("max is %d\n",c);}}

Page 16: 第 7 章  函数与预处理命令

2005 3年 月

16

函数的返回值是通过 return 语句带回到主调函数的

功能:功能:终止函数的运行,返回主调函数,若有返终止函数的运行,返回主调函数,若有返回值,将返回值带回主调函数。回值,将返回值带回主调函数。说明:说明:① ① 若函数没有返回值,若函数没有返回值, returnreturn 语句可以省略。语句可以省略。

② ② returnreturn 语句中的表达式类型一般应和函数的类语句中的表达式类型一般应和函数的类型一致,型一致,如果不一致,系统自动将表达式类型转换为函数类型。。

⑵ ⑵ 函数的返回值函数的返回值

return return 语句格式:语句格式:return return (( 表达式表达式 );); 或 或 return return 表达表达式式 ;;或 或 return;return;

Page 17: 第 7 章  函数与预处理命令

2005 3年 月

17【【例例 7.87.8 】】计算并输出圆的面积。计算并输出圆的面积。s(int r)s(int r)

{ return 3.14*r*r;}{ return 3.14*r*r;}

main( )main( )

{ int r,area;{ int r,area;

scanf("%d",&r);scanf("%d",&r);

printf("%d\n",s(r));printf("%d\n",s(r));

}}

自动转换自动转换为为 intint 型 型

思考:若要得到单精度实型的圆面积,程序应如何修改

程序运行情况如下:212

Page 18: 第 7 章  函数与预处理命令

2005 3年 月

18

7.2.4 7.2.4 对被调函数的声明和函数原型对被调函数的声明和函数原型变量要变量要先定义后使用先定义后使用,,

函数也如此函数也如此。即。即被调函数的被调函数的定义要出现在主调函数的定定义要出现在主调函数的定义之前义之前。如。如 swapswap 函数函数 ::

允许整型函数(且参数允许整型函数(且参数也是整型)的定义出现在主也是整型)的定义出现在主调函数之后。如调函数之后。如 maxmax 函数函数 ::

如果非整型函数在主调如果非整型函数在主调函数之后定义,则应在主调函数之后定义,则应在主调函数中或主调函数之前对函数中或主调函数之前对被被调函数进行声明。调函数进行声明。

void swap(int x, int y) { …}main( ){ … swap(a,b); }

main( )main( ){ …{ … c=max(a,b);c=max(a,b);}}max(int x,int y)max(int x,int y){ …}{ …}

Page 19: 第 7 章  函数与预处理命令

2005 3年 月

19对被调函数进行声明的一般形式对被调函数进行声明的一般形式 函数类型 函数名(参数类型 1 参数名 1 ,…) ;或 函数类型 函数名(参数类型 1 ,参数类型 2 ,…) ;

思考思考 ::

以下哪种情况需要以下哪种情况需要在主调函数中在主调函数中对被调函数对被调函数声明声明 被调函数定义在前被调函数定义在前,,主调函数定义在后主调函数定义在后。。 主调函数定义在前主调函数定义在前,,被调函数定义在后被调函数定义在后,且被调,且被调

函数的类型不是整型的。函数的类型不是整型的。 被调函数定义在后被调函数定义在后,但被调函数的类型是,但被调函数的类型是整型整型。。

第二种形式省略了参数名,此种形式也称为函数的原型。

Page 20: 第 7 章  函数与预处理命令

2005 3年 月

20

main( ){ void calc(float x,float y,char opr); float a,b; char opr; printf("\nInput expression:"); scanf("%f%c%f",&a,&opr,&b); calc(a,b,opr);}void calc(float x,float y,char opr){ switch(opr) { case '+':printf("%5.2f%c%5.2f=%6.2f\n",x,opr,y,x+y);return; case '-':printf("%5.2f%c%5.2f=%6.2f\n",x,opr,y,x-y);return; case '*':printf("%5.2f%c%5.2f=%6.2f\n",x,opr,y,x*y);return; case '/':printf("%5.2f%c%5.2f=%6.2f\n",x,opr,y,x/y);return; default :printf("Operator err! \n"); }}

对被调函数的声明

【【例例 7.97.9 】】计算并输出两个数的和、差、积、商。计算并输出两个数的和、差、积、商。

Page 21: 第 7 章  函数与预处理命令

2005 3年 月

21

图 7.2 验证哥德巴赫猜想

输入 n 的值 for (a=6; a<=n; a+=2) for(b=3;b<=a/2;b+=2) b 是素数 ?

T F c=a-b

c 是素数? T F 输出: a 、 b 、 c 的值 break;

【【例例 7.107.10 】 】 哥德巴赫猜想之一是任何一个大于哥德巴赫猜想之一是任何一个大于 55 的的偶数都可以表示为两个素数之和。验证这一论断。偶数都可以表示为两个素数之和。验证这一论断。

Page 22: 第 7 章  函数与预处理命令

2005 3年 月

22

#include "math.h"#include "math.h"int prime(int n);int prime(int n); main( )main( ){ int a,b,c,n;{ int a,b,c,n; scanf("%d",&n);scanf("%d",&n); for (a=6; a<=n; a+=2)for (a=6; a<=n; a+=2) for (b=3; b<=a/2; b+=2) for (b=3; b<=a/2; b+=2) if (prime(b)) if (prime(b)) { c=a-b;{ c=a-b; if (prime(c))if (prime(c)) { printf("%d=%d+%d\n",a,b,c);{ printf("%d=%d+%d\n",a,b,c); break; }break; } }}}}

/* 穷举法判断素数 */int prime(int n){ int i; for (i=2; i<=sqrt(n); i+

+) if (n%i==0) return 0; return 1;}

可以在 main 函数的前面对prime 函数进行声明。实际上,该声明可以省略,为什么?

程序如下:程序如下:

Page 23: 第 7 章  函数与预处理命令

2005 3年 月

237.3 7.3 数组作函数参数数组作函数参数7.3.1 一维数组元素作函数参数

main( ){ int a[5],i,m ; for (i=0; i<5; i++) scanf("%d",&a[i]); m=a[0]; for (i=1; i<5; i++) m=min(m,a[i]); printf("%d\n", m);}

【例 7.11 】求 5 个数中的最小值。int min(int x, int y){ return (x<y?x:y); }

用打擂台方法求用打擂台方法求最小值。最小值。 mm 相相当于擂主当于擂主

Page 24: 第 7 章  函数与预处理命令

2005 3年 月

247.3.2 一维数组名作函数参数 数组名数组名表示数组在内存中的表示数组在内存中的起始地址起始地址。 。 例如例如 ::数组 a 在内存中从 20002000 地址开始存放 , 则 a 的值

为 2000 。 2000 是地址值,是指针类型的数据(第 8 中将介绍指针类型),不能把它看成是整型或其他类型数据。

实参是数组名实参是数组名,形参也应定义为数组形式数组形式,形参数组的长度可以省略,但 [ ] 不能省,否则就不是数组形式了。

【【例例 7.127.12 】】用冒泡法将用冒泡法将 1010 个整数排序。个整数排序。

Page 25: 第 7 章  函数与预处理命令

2005 3年 月

25void sort(int b[ ],int n); void sort(int b[ ],int n);

void printarr(int b[ ]); void printarr(int b[ ]);

main( )main( ){ int a[10] = { int a[10] =

{11,22,63,97,58,80,45, {11,22,63,97,58,80,45, 32,73,36};32,73,36};

printf("Before sort:\printf("Before sort:\n");n");

printarr(a);printarr(a); sort(a,10);sort(a,10); printf("After sort:\n");printf("After sort:\n"); printarr(a);printarr(a);}}

void printarr(int void printarr(int b[10]b[10]) )

{ int i;{ int i;

for (i=0; i<10; i++)for (i=0; i<10; i++)

printf("%5d",b[i]);printf("%5d",b[i]);

printf("\n");}printf("\n");}

void sort(void sort(int b[ ], int nint b[ ], int n) )

{ int i,j,t;{ int i,j,t;

for (i=1; i<n; i++) for (i=1; i<n; i++)

for (j=0; j<n-i; j++ )for (j=0; j<n-i; j++ )

if (b[j]>b[j+1]) if (b[j]>b[j+1]) { t=b[j];b[j]=b[j+1];b[j+1]=t; }{ t=b[j];b[j]=b[j+1];b[j+1]=t; }

}}

Page 26: 第 7 章  函数与预处理命令

2005 3年 月

26

a[0] a[1] a[2] a[3] a[4 ] a[5] a[6] a[7] a[8] a[9]

b[0] b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8] b[9]

11 22 63 97 58 80 45 32 73 36

(a) 排序前a[0] a[1] a[2] a[3] a[4 ] a[5] a[6] a[7] a[8] a[9]

b[0] b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8] b[9]

11 22 32 36 45 58 63 73 80 97

(b) 排序后

图 7.3 调用 sort 函数

20002000bb形参 形参 b b 实际是一个实际是一个

可以存放地址的变量可以存放地址的变量a:2000

实参赋给形参

首地址: 2000首地址: 2000

Page 27: 第 7 章  函数与预处理命令

2005 3年 月

27

#include "stdio.h"main( ){ void scat(char str1[ ],char str2[ ]); char s1[50],s2[50]; int i,k; printf("Input s1:"); gets(s1); printf("Input s2:"); gets(s2); scat(s1,s2); printf("Output s1:%s\n",s1); printf("Output s2:%s\n",s2);}}

void scat(char void scat(char str1[ ],str1[ ],char char str2[ ])str2[ ])

{ int i=0,k=0;{ int i=0,k=0;

while (str1[i]!='\0') i++;while (str1[i]!='\0') i++;

while (str2[k]!='\0')while (str2[k]!='\0')

{ str1[i]=str2[k];{ str1[i]=str2[k];

i++; k++;i++; k++;

}}

str1[i]='\0';str1[i]='\0';

}}

scatscat 函数还可简化为:函数还可简化为:void scat(char str1[ ],char void scat(char str1[ ],char

str2[ ])str2[ ])

{ int i=0,k=0;{ int i=0,k=0;

while (str1[i]) i++;while (str1[i]) i++;

while (str1[i++]=str2[k++]);while (str1[i++]=str2[k++]);

}}

【例 7.13 】编程序,实现字符串连接。

Page 28: 第 7 章  函数与预处理命令

2005 3年 月

28

以二维数组为例。二维数组名作实参时,对应以二维数组为例。二维数组名作实参时,对应的形参也应该定义为一个二维数组形式。对形参数的形参也应该定义为一个二维数组形式。对形参数组定义时可以指定每一维的大小,也可以省略第一组定义时可以指定每一维的大小,也可以省略第一维的大小说明。维的大小说明。

【【例例 7.147.14 】】编程序,将矩阵转置。设转置前为编程序,将矩阵转置。设转置前为aa矩阵,转置后为矩阵,转置后为 bb矩阵,如下所示:矩阵,如下所示:

a=a=1 2 3 4 1 2 3 4 5 6 7 85 6 7 89 10 11 129 10 11 12

1 1 55 992 2 66 10103 3 77 11114 4 88 1212

b=b=

思路:将思路:将 a[0][0]a[0][0]b[0][0]b[0][0] ,, a[0][1]a[0][1] b[1][0] b[1][0] ,, a[0][2]a[0][2]b[2][0]b[2][0] ,,a[1][0]a[1][0]b[0][1]b[0][1] ,,……,, a[i][j]a[i][j]b[j][i]b[j][i] ,,……。。

7.3.3 多维数组作函数参数

Page 29: 第 7 章  函数与预处理命令

2005 3年 月

29void turn( );

main( )

{ int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

int i,j,b[4][3];

turn(a,b);

printf("array b:\n");

for (i=0; i<4; i++)

{ for (j=0; j<3; j++)

printf("%5d",b[i][j]);

printf("\n");

}

}

/* /* 矩阵转置函数 矩阵转置函数 **//

void turn(int void turn(int arra[ ][4],arra[ ][4],int int arrb[ ][3])arrb[ ][3])

{ int r, c;{ int r, c;

for (r=0; r<3;r++)for (r=0; r<3;r++)

for (c=0; c<4; c++)for (c=0; c<4; c++)

arrb[c][r]=arra[r][c];arrb[c][r]=arra[r][c];

}}

Page 30: 第 7 章  函数与预处理命令

2005 3年 月

307.4 7.4 函数的嵌套调用和递归调用函数的嵌套调用和递归调用

mainmain 函数函数

{{ …………

调用函数 调用函数 AA;;

…………

}}

函数 函数 AA

{ { …………

调用函数 调用函数 BB;;

…………

}}

函数 函数 BB

{{ …………

…… ……

…… ……

}}

7.4.1 函数的嵌套调用

Page 31: 第 7 章  函数与预处理命令

2005 3年 月

31【【例例 7.157.15 】】函数的嵌套调用函数的嵌套调用

main( )main( )

{ int n=3;{ int n=3;

printf ("%d\n",printf ("%d\n",sub1(n));sub1(n));

}}

sub1(int n)sub1(int n)

{ int i,a=0;{ int i,a=0;

for (i=n; i>0; i--) for (i=n; i>0; i--)

a+=a+=sub2(i);sub2(i);

return a ;return a ;

}}

sub2(int n)sub2(int n)

{ {

return n+1; return n+1;

}}程序输出结果:9

Page 32: 第 7 章  函数与预处理命令

2005 3年 月

327.4.2 函数的递归调用1.递归的基本概念

递归调用:一个函数直接或间接地调用了它本身,就称为函数的递归调用。递归函数:在函数体内调用该函数本身。

int sub(int x){ int y,z; …… if( …… ) z=sub(y); else { …… } return ;}

例如:

直接调用 sub函数本身

Page 33: 第 7 章  函数与预处理命令

2005 3年 月

3322 .递归函数的执行过程.递归函数的执行过程

【例 7.16 】编一递归函数求 n! 。思路:以求 4 的阶乘为例 :4!=4*3! , 3!=3*2! , 2!=2*1! , 1!=1 , 0!=1 。递归结束条件:当 n=1 或 n=0 时, n!=1 。递归公式:

n! =n! =1 1 (( n=0, n=0, 11 ))n×(n-1)! n×(n-1)! (( n>1n>1 ))

Page 34: 第 7 章  函数与预处理命令

2005 3年 月

34程序如下:程序如下:

float fact (int n)

{ float f=0;

if(n<0)

printf("n<0,error!");

else if (n==0 || n==1)

f=1;

else f=fact(n-1)*n;

return (f);

}

main( ){ int n; float y; printf("\nInput n:"); scanf("%d",&n); y=fact(n); printf("%d!=%-10.0f\n",n,y);}运行情况如下:Input a integer number:44!=24

Page 35: 第 7 章  函数与预处理命令

2005 3年 月

35递归调用过程递归调用过程 回 推main( ) fact(4) fact(3) fact(2) fact(1){ { { { { … … … … … y=fact(4); f=4*fact(3); f=3*fact(2); f=2*fact(1); f=1; … … … … … return 24 return 6 return 2 return 1} } } } }

递 推

Page 36: 第 7 章  函数与预处理命令

2005 3年 月

3633 .编制递归函数的方法.编制递归函数的方法⑴ ⑴ 数值型问题递归函数的编程方法数值型问题递归函数的编程方法对于数值型问题,首先要找出解题的数学公式,对于数值型问题,首先要找出解题的数学公式,这个公式必须是这个公式必须是递归定义递归定义的,且所处理的的,且所处理的对象要有对象要有规律地递增或递减规律地递增或递减,然后确定,然后确定递归结束条件递归结束条件。。【【例例 7.177.17 】】编一递归函数求编一递归函数求 xxnn 。。思路:首先把思路:首先把 xxnn转化成递归定义的公式转化成递归定义的公式

xnn = 1 ( n=0 )x × xn - 1 ( n>0 )再找出递归结束条件:当再找出递归结束条件:当 n=0n=0 时,时, xxn=1=1 。。

Page 37: 第 7 章  函数与预处理命令

2005 3年 月

37程序如下:程序如下:long xn(int x,int n)long xn(int x,int n){ long f=0;{ long f=0; if (n<0) printf("n<0,data error!\n");if (n<0) printf("n<0,data error!\n"); else if (n==0) f=1;else if (n==0) f=1; else f=x*xn(x,n-1);else f=x*xn(x,n-1); return (f);return (f);}}main( )main( ){ int n,x; long y;{ int n,x; long y; scanf("%d,%d",&x,&n);scanf("%d,%d",&x,&n); y=xn(x,n);y=xn(x,n); printf("%ld\n",y);printf("%ld\n",y);}}

程序运行情况如下:2,101024

Page 38: 第 7 章  函数与预处理命令

2005 3年 月

38⑵ 非数值型问题递归函数的编程方法

有些问题不能直接用数学公式求解。非有些问题不能直接用数学公式求解。非数值型问题比数值型问题更难找出递归的算数值型问题比数值型问题更难找出递归的算法。它不能用一个递归公式表示。解决这类法。它不能用一个递归公式表示。解决这类问题首先要把问题将大化小,将繁化简。将问题首先要把问题将大化小,将繁化简。将一个复杂的问题化解成若干个相对简单的小一个复杂的问题化解成若干个相对简单的小问题,而某个小问题的解法与原问题解法相问题,而某个小问题的解法与原问题解法相同,并且越来越简单直至有确定的解。同,并且越来越简单直至有确定的解。【【例例 7.187.18 】】编制一递归函数,将一个十进制正编制一递归函数,将一个十进制正整数(如:整数(如: 1561315613 )转换成八进制数形式输出。)转换成八进制数形式输出。

Page 39: 第 7 章  函数与预处理命令

2005 3年 月

39

思路:十进制整数转换成八进制整数的方法是思路:十进制整数转换成八进制整数的方法是除除 88逆向取余逆向取余。如图。如图 7.57.5 所示。所示。

余数: 商:15613%8=5 15613/8=19511951%8=7 1951/8=243243%8=3 243/8=3030%8=6 30/8=33%8=3 3/8=0结果: 36375

图 7.5 十进制转换成八进制

⑵ 非数值型问题递归函数的编程方法( 续 )

Page 40: 第 7 章  函数与预处理命令

2005 3年 月

40

该题实际上是要把一个十进制数除以该题实际上是要把一个十进制数除以 88 得到的余得到的余数逆向输出。就是先得到的余数后输出,最后得数逆向输出。就是先得到的余数后输出,最后得到的余数最先输出。到的余数最先输出。

我们先由大化小:求八进制数变成求一系列余数我们先由大化小:求八进制数变成求一系列余数的问题。求第一个余数是将的问题。求第一个余数是将 1561315613 除以除以 88 取余,取余,因为先得到的余数后输出,所以把这个余数存在因为先得到的余数后输出,所以把这个余数存在一个变量一个变量 mm 中,接下去求下一个余数。和求第中,接下去求下一个余数。和求第一个余数的方法相同,只是被除数变成了一个余数的方法相同,只是被除数变成了 1561315613除以除以 88 的整数商的整数商 19511951 。因此,这是一个递归调。因此,这是一个递归调用的问题。定义变量用的问题。定义变量 mm 存放余数,存放余数, xx 存放被除存放被除数。递归算法描述如下:数。递归算法描述如下:

⑵ 非数值型问题递归函数的编程方法( 续 )

Page 41: 第 7 章  函数与预处理命令

2005 3年 月

41

① ① 先求出余数先求出余数 mm :: m=x%8;m=x%8;

② ② 求求 xx 除以除以 88 取余后的整数商:取余后的整数商: x=x/8;x=x/8;

③ ③ 如果如果 xx 不等于不等于 00 ,递归调用该函数,否,递归调用该函数,否则执行④。则执行④。

④ ④ 输出余数输出余数 mm 。。⑤ ⑤ 返回调用点。返回调用点。

⑵ 非数值型问题递归函数的编程方法( 续 )

Page 42: 第 7 章  函数与预处理命令

2005 3年 月

42程序如下:程序如下:#include "stdio.h"#include "stdio.h"void dtoo(int x)void dtoo(int x){ int m;{ int m; m=x%8;m=x%8; x=x/8;x=x/8; if (x!=0) dtoo(x);if (x!=0) dtoo(x); printf("%d",m);printf("%d",m);}}main( )main( ){ int n;{ int n; scanf("%d",&n);scanf("%d",&n); printf("%d=(",n);printf("%d=(",n); dtoo(n);dtoo(n); printf(")8\n");printf(")8\n");}}

程序运行情况如下:1561315613=(36375)8

Page 43: 第 7 章  函数与预处理命令

2005 3年 月

437.5 7.5 局部变量和全局变量及其作用域局部变量和全局变量及其作用域

7.5.1 变量的作用域

7.5.2 局部变量及其作用域

变量的作用域:变量在程序中可以被使用的范围。根据变量的作用域可以将变量分为局部变量和全局变量。

局部变量(内部变量内部变量):在函数内或复合语句内定义的变量以及形参形参。作用域:函数内或复合语句内。

【例 7.19 】分析下面程序的运行结果及变量的作用域。

问题:问题:一个变量在程序的哪个函数中都能使用吗?一个变量在程序的哪个函数中都能使用吗?

Page 44: 第 7 章  函数与预处理命令

2005 3年 月

44void sub(int a,int b)

{ int c;

a=a+b; b=b+a; c=b-a;

printf("sub:\ta=%d b= %d c= %d\n",a,b,c); }

局部变量局部变量

main( )

{ int a=1,b=1,c=1;

printf("main:\ta=%d b= %d c= %d\n",a,b,c);

sub(a,b);

printf("main:\ta=%d b= %d c= %d\n",a,b,c);

{ int a=2,b=2;

printf("comp:\ta=%d b= %d c= %d\n",a,b,c); } printf("main:\ta=%d b= %d c= %d\n",a,b,c); }

局部变量局部变量

局部变量局部变量

分程

序”

或“

序块”

程序输出结果:main: a=1 b= 1 c= 1sub: a=2 b= 3 c= 1main: a=1 b= 1 c= 1comp: a=2 b= 2 c= 1main: a=1 b= 1 c= 1

Page 45: 第 7 章  函数与预处理命令

2005 3年 月

457.5.3 全局变量及其作用域全局变量(外部变量外部变量):在函数外部定义的变量。作用域:从定义变量的位置开始到本源文件从定义变量的位置开始到本源文件结束结束。如在其作用域内的函数或分程序中定义了同名局部变量,则在局部变量的作用域内,同名全局变量暂时不起作用。

【例 7.20 】全局变量和局部变量的作用域。

Page 46: 第 7 章  函数与预处理命令

2005 3年 月

46int a = 5;void f(int x, int y){ int b,c; b=a+x; c=a-y;printf("%d\t%d\t%d\n",a,b,c); }

局部变量局部变量

main( )

{ int b=6,c=7;

f(b,c);

printf("%d\t%d\t%d\n",a,b,c);

{ int a=9,b=8;

printf("%d\t%d\t%d\n",a,b,c);

{ c=10;

printf("%d\t%d\t%d\n", a,b,c);

}

printf("%d\t%d\t%d\n",a,b,c);

}

printf("%d\t%d\t%d\n",a,b,c);

}

局部变量局部变量

局部变量局部变量

程序输出结果:5 11 -25 6 79 8 79 8 109 8 105 6 10

全局变量

Page 47: 第 7 章  函数与预处理命令

2005 3年 月

477.6 7.6 变量的存储类别及变量的生存期变量的存储类别及变量的生存期

7.6.1 变量的生存期与变量的存储分类

变量的生存期:变量在内存中占据存储空间的时间。

思考思考:: 1. 1. 何时何时为变量分配内存单元为变量分配内存单元?? 2. 2. 将变量分配在内存的什么将变量分配在内存的什么区域区域?? 33. . 变量占据内存的变量占据内存的时间时间((生存期生存期)?)?

程序代码区

静态存储区

动态存储区存储分配

动态存储变量

静态存储变量

Page 48: 第 7 章  函数与预处理命令

2005 3年 月

487.6.2 变量的存储类别

变量的属性

数据类型:数据类型:决定为变量分配内存单元的长度,决定为变量分配内存单元的长度,数据的存放形式,数据的存放形式,数的范围。数的范围。

存储类别:存储类别:决定了变量的生存期,决定了变量的生存期,给它分配在哪个存储区。给它分配在哪个存储区。

Page 49: 第 7 章  函数与预处理命令

2005 3年 月

49变量定义语句的一般形式存储类别 数据类型 变量名存储类别 数据类型 变量名 1, … , 1, … , 变量名变量名 n ;n ;

autoauto (自动的)(自动的) registerregister (寄存器(寄存器的)的) staticstatic (静态的)(静态的) externextern (外部(外部的)的)

1.自动变量( auto 类别) 局部变量可以定义为自动变量。

main(){int x,y; … }

main(){auto int x,y; … }

自动变量自动变量

等价

可省

Page 50: 第 7 章  函数与预处理命令

2005 3年 月

50

⑴ ⑴ 内存分配内存分配调用函数或执行分程序时在调用函数或执行分程序时在动态存储区动态存储区为其分配存为其分配存储单元,函数或分程序执行结束,所占内存空间即储单元,函数或分程序执行结束,所占内存空间即刻释放。刻释放。⑵ ⑵ 变量的初值变量的初值定义变量时若没赋初值,变量的定义变量时若没赋初值,变量的初值不确定初值不确定;如果;如果赋初值则每次函数被调用时执行一次赋值操作。赋初值则每次函数被调用时执行一次赋值操作。⑶ ⑶ 生存期生存期 在函数或分程序执行期间。在函数或分程序执行期间。⑷ ⑷ 作用域作用域自动变量所在的函数内或分程序内。自动变量所在的函数内或分程序内。

自动变量自动变量

Page 51: 第 7 章  函数与预处理命令

2005 3年 月

5122 .静态变量(.静态变量( staticstatic 类别)类别)除形参外,局部变量和全局变量都可以定义为静除形参外,局部变量和全局变量都可以定义为静态变量。态变量。

局部静态变量(或称内部静态变量)局部静态变量(或称内部静态变量)全局静态变量(或称外部静态变量)

静态变量静态变量静态变量静态变量

static int a; main( ){ float x,y; … }f( ){ static int b=1; ……}

全局静态变量

局部静态变量局部静态变量

自动变量自动变量不能省

Page 52: 第 7 章  函数与预处理命令

2005 3年 月

52

⑴ ⑴ 内存分配内存分配编译时,将其分配在内存的静态存储区中,程序运行结束释放该单元。⑵ ⑵ 静态变量的初值静态变量的初值若定义时未赋初值,在编译时,系统自动赋初值为0 ;若定义时赋初值,则仅在编译时赋初值一次,程序运行后不再给变量赋初值 。⑶ ⑶ 生存期生存期整个程序的执行期间。⑷ ⑷ 作用域作用域局部静态变量的作用域是它所在的函数或分程序。全局静态变量的作用域是从定义处开始到本源文件结束。

静态变量静态变量

Page 53: 第 7 章  函数与预处理命令

2005 3年 月

53

int c;static int a; main( ){ float x,yx,y; … }char s;f( ){ static int b=1; ……}

3.3.外部变量(外部变量( externextern 类别)类别)在函数外定义的变量若没有用 在函数外定义的变量若没有用 staticstatic 说明,则是外说明,则是外部变量。外部变量只能隐式定义为部变量。外部变量只能隐式定义为 externextern 类别,不类别,不能显式定义。能显式定义。

全局静态变量

自动变量自动变量

局部静态变量局部静态变量

外部变量

Page 54: 第 7 章  函数与预处理命令

2005 3年 月

54

⑴ ⑴ 内存分配内存分配编译时,将其分配在静态存储区,程序编译时,将其分配在静态存储区,程序运行结束释放该单元。运行结束释放该单元。⑵ ⑵ 变量的初值变量的初值若定义变量时未赋初值,在编译时,系若定义变量时未赋初值,在编译时,系统自动赋初值为统自动赋初值为 00 。。⑶ ⑶ 生存期生存期整个程序的执行期间。整个程序的执行期间。⑷ ⑷ 作用域作用域从定义处开始到本源文件结束。从定义处开始到本源文件结束。此外,还可以用此外,还可以用 externextern 进行声明,以进行声明,以使其作用域扩大到该程序的其它文件中。使其作用域扩大到该程序的其它文件中。

外部变量外部变量

问题:全局静态变量的作用域可以扩展到本程序的其它文件吗?

Page 55: 第 7 章  函数与预处理命令

2005 3年 月

55外部变量声明的一般格式 externextern 数据类型 变量名数据类型 变量名 11 ,,……,变量名,变量名 nn;;或或 externextern 变量名变量名 11 ,,……,变量名,变量名 nn;;

注意:①外部变量声明用关键字 extern,而外部变量

的定义不能用 extern,只能隐式定义。②定义外部变量时,系统要给变量分配存储空间,而对外部变量声明时,系统不分配存储空间,只是让编译系统知道该变量是一个已经定义过的外部变量,与函数声明的作用类似。

Page 56: 第 7 章  函数与预处理命令

2005 3年 月

56

int p=1,q=5; int p=1,q=5; float f1(int a) float f1(int a) { extern char c1,c2; { extern char c1,c2;

…… ……}}char c1,c2; char c1,c2; char f2(int x,int y)char f2(int x,int y){ ……{ ……}}main( )main( ){ ……{ ……}}

外部变量声明

定义外部变量定义外部变量

定义外部变量定义外部变量

思考:在 f1 函数中声明 c1 、c2 的作用是什么?如何修改程序使所有函数都可以使用外部变量而又不需要声明?

【例 7.24 】在一个文件内声明外部变量。

Page 57: 第 7 章  函数与预处理命令

2005 3年 月

57【例 7.25 】在多文件的程序中声明外部变量。file1.cfile1.c 文件中程序如下:文件中程序如下:int i;main( ){ void f1( ),f2( ),f3( ); i=1; f1( ); printf("\tmain: i=%d",i); f2( ); printf("\tmain: i=%d",i); f3( ); printf("\tmain: i=%d\n",i);}void f1( ){ i++; printf("\nf1: i=%d",i);}

file2.c 文件中程序如下:extern int i; void f2( ){ int i=3; printf("\nf2: i=%d",i);}void f3( ){ i=3; printf("\nf3: i=%d",i);}

程序输出结果:f1: i=2 main: i=2f2: i=3 main: i=2f3: i=3 main: i=3

声明外部变量定义外部变量

Page 58: 第 7 章  函数与预处理命令

2005 3年 月

584.4.寄存器变量(寄存器变量( registerregister 类别)类别)只有只有函数内定义的变量或形参函数内定义的变量或形参可以定义为寄存器可以定义为寄存器变量。寄存器变量的值保存在变量。寄存器变量的值保存在 CPUCPU 的寄存器中。的寄存器中。受寄存器长度的限制,受寄存器长度的限制,寄存器变量只能是寄存器变量只能是charchar 、、 intint 和指针类型的变量。和指针类型的变量。【例 7.26 】寄存器变量的使用。main( ){ long int sum=0; register int i; for (i=1; i<=1000; i++) sum+=i; printf("sum=%ld\n ",sum);}

程序输出结果:sum=500500

Page 59: 第 7 章  函数与预处理命令

2005 3年 月

597.6.3 归纳变量的分类1 .按照变量的作用域对变量分类⑴ 局部变量⑵ 全局变量2 .按照变量的生存期对变量分类⑴ 静态存储变量包括:局部静态变量和全局静态变量⑵ 动态存储变量包括:自动变量

Page 60: 第 7 章  函数与预处理命令

2005 3年 月

607.7 函数的存储分类

外部函数:外部函数:extern int fan(char a,char b){ …… }

静态函数:静态函数:static int func( )

{ …… }

外部函数外部函数和和静态函数静态函数区别:区别:外部函数允许本程序其他文件中的函数调用外部函数允许本程序其他文件中的函数调用( 与外部变量类似 ) 。静态函数静态函数禁止禁止本程序其他文件中的函数调用本程序其他文件中的函数调用( 与外部静态变量类似 ) 。

extern 可以省略

Page 61: 第 7 章  函数与预处理命令

2005 3年 月

61

源文件 源文件 *.*.cc

运行文件运行文件*.*.exeexe

编译编译编译编译编译编译目标文件目标文件*.*.objobj 连接连接编译编译 连接连接连接连接

编译预处理 编 译编译预处理 编 译

编译预处理包括:编译预处理包括: 宏定义宏定义 文件包含文件包含 条件编译条件编译

7.8 7.8 编译预处理编译预处理

Page 62: 第 7 章  函数与预处理命令

2005 3年 月

627.8 7.8 编译预处理(续)编译预处理(续)

源文件 源文件 *.*.cc

运行文件运行文件*.*.exeexe

目标文件目标文件*.*.objobj

编译编译 连接连接

main()main()

{ float r,s,c;{ float r,s,c;

scanf(“%f”,&r);scanf(“%f”,&r);

s = r * r * s = r * r * 3.143.14;;

c = 2 * r * c = 2 * r * 3.143.14;;

printf(“s=%f, c=printf(“s=%f, c=%f”,s,c);%f”,s,c);

}}

如何修改圆周率

Page 63: 第 7 章  函数与预处理命令

2005 3年 月

63

##definedefine 宏名宏名 字符串字符串

宏定义的功能:宏定义的功能: 在进行编译前,用在进行编译前,用字符串字符串原样替换程序中的原样替换程序中的宏名宏名。。 这个替换过程称为“这个替换过程称为“宏替换宏替换”或“”或“宏展开宏展开”,”,字符串字符串也称为替换文本。也称为替换文本。

命令的一般格式:命令的一般格式:7.8.1 7.8.1 不带参数的宏定义不带参数的宏定义7.8 7.8 编译预处理(续)编译预处理(续)

Page 64: 第 7 章  函数与预处理命令

2005 3年 月

647.8 7.8 编译预处理(续)编译预处理(续)

例如:例如: #define #define PIPI 3.143.14

main()main()

{ float r,s,c;{ float r,s,c;

scanf(“%f”,&r);scanf(“%f”,&r);

s = r * r * s = r * r * PIPI;;

c = 2 * r * c = 2 * r * PIPI;;

printf(“s=%f, c=printf(“s=%f, c=%f”,s,c);%f”,s,c);

}}

替换

3.143.14

3.143.14

编 译编 译

Page 65: 第 7 章  函数与预处理命令

2005 3年 月

657.8 7.8 编译预处理(续)编译预处理(续)

②② 为了增加程序的可读性,建议宏名用大写字母,其为了增加程序的可读性,建议宏名用大写字母,其他的标识符用小写字母。他的标识符用小写字母。

③③双引号中有与宏名相同的字符串不进行替换。双引号中有与宏名相同的字符串不进行替换。④④已经定义的宏名可以被后定义的宏名引用。在预处已经定义的宏名可以被后定义的宏名引用。在预处

理时将层层进行替换。理时将层层进行替换。

说明说明 ::① ① 宏定义的作用域是从定义处开始到源文件结束, 宏定义的作用域是从定义处开始到源文件结束,

但根据需要可用 但根据需要可用 undefundef 命令终止其作用域。形式命令终止其作用域。形式为为 :: #undef #undef 宏名宏名

Page 66: 第 7 章  函数与预处理命令

2005 3年 月

66【【例例 7.297.29 】】不带参数的宏定义。不带参数的宏定义。

源程序:源程序:#define PI 3.14#define PI 3.14

#define S PI*r*r#define S PI*r*r

#define V 4*S*r/3#define V 4*S*r/3

main( )main( )

{float r;{float r;

printf("\nInput r:");printf("\nInput r:");

scanf("%f",&r);scanf("%f",&r);

printf("S=%.2f V=%.2f\printf("S=%.2f V=%.2f\n",n",SS,,VV););

}}

编译预处理后的程序:编译预处理后的程序:main( )

{ float r;

printf("\nInput r:");

scanf("%f",&r);

printf("S=%.2f V=%.2f\n",

3.14*r*r, 4* 3.14*r*r *r/3);

}S VS V

进入编译进入编译

Page 67: 第 7 章  函数与预处理命令

2005 3年 月

67

命令的一般形式7.8.2 7.8.2 带参数的宏定义带参数的宏定义

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

功能 : 在编译预处理时,把源程序中所有带参数的宏名用宏定义中的字符串替换,并且用宏名后圆括号中的实参替换字符串中的形参。

例如: #define MAX(X,Y) ((X)>(Y)?(X):(Y))

7.8 7.8 编译预处理(续)编译预处理(续)

Page 68: 第 7 章  函数与预处理命令

2005 3年 月

687.8 7.8 编译预处理(续)编译预处理(续)【【例例 7.307.30 】】带参数的宏定义。带参数的宏定义。#define MAX(x,y) #define MAX(x,y) ((x)>(y)?(x):(y))((x)>(y)?(x):(y))main( )main( ){ …{ … printf("%d\n",a,b, printf("%d\n",a,b, MAX(a,b)MAX(a,b));); printf("%d\n",printf("%d\n",MAX(a+m,b+n)MAX(a+m,b+n)););}}

分两次替换:① 将宏名 MAX(a,b)MAX(a,b) 替换成字符串 ((x)>(y)?(x):(y))((x)>(y)?(x):(y)) 。② 用实参 a替换形参 x ,实参 b替换形参 y 。程序中的两个 printf 语句被展开为:printf("%d\n", ((a)>(b)?(a):(b))); printf("%d\n", ((a+m)>(b+n)?( a+m):( b+n)));

Page 69: 第 7 章  函数与预处理命令

2005 3年 月

697.8 7.8 编译预处理(续)编译预处理(续)【【例例 7.317.31 】】分析下面程序运行后的输出结果。分析下面程序运行后的输出结果。#define MA(x) #define MA(x) x*(x-1)x*(x-1)

main( )main( )

{ int a=1,b=2;{ int a=1,b=2;

printf("%d\n", printf("%d\n", MA(1+a+b)MA(1+a+b)););

}}

分两次替换: ①MA(1+a+b)MA(1+a+b) 用 x*(x-1)x*(x-1) 替换。② 用 1+a+b替换 x 。printf 语句被展开为:printf("%d\n", 1+a+b*(1+a+b-1));

特别注意:由于替换文本中的 x 没有用括号括起,因此, 1+a+b也不能用括号括起。

程序输出结果: 8

Page 70: 第 7 章  函数与预处理命令

2005 3年 月

707.8 7.8 编译预处理(续)编译预处理(续)

1.命令的一般形式1.命令的一般形式格式格式 11 : : #include #include << 文件名文件名 >>

格式格式 22 : : #include #include "" 文件名文件名 ""

7.8.3 7.8.3 文件包含文件包含    文件包含是将指定的某个源文件的内容全部包文件包含是将指定的某个源文件的内容全部包含到当前文件中。用含到当前文件中。用 includeinclude 命令实现。命令实现。

 用 用格式格式 11 ,预处理程序仅在,预处理程序仅在 \TC\INCLUDE\TC\INCLUDE目录下目录下查找指定文件。查找指定文件。 用 用格式格式 22 ,预处理程序首先在,预处理程序首先在当前目录中当前目录中查找指定查找指定文件,若找不到再到文件,若找不到再到 \TC\INCLUDE\TC\INCLUDE目录中查找。目录中查找。

Page 71: 第 7 章  函数与预处理命令

2005 3年 月

717.8 7.8 编译预处理(续)编译预处理(续)

例如:调用例如:调用 sin(x) sin(x) 函数时,要在程序的开头使用函数时,要在程序的开头使用如下命令:如下命令:#include <#include <math.hmath.h> >

在预处理时,用在预处理时,用 math.hmath.h 文件内容替换 文件内容替换 #include <math.h>#include <math.h> 命令行。命令行。

22 .功能.功能  在预处理时,将  在预处理时,将 includeinclude 命令后指定文件的命令后指定文件的内容替换该命令行。内容替换该命令行。

Page 72: 第 7 章  函数与预处理命令

2005 3年 月

727.9 7.9 多文件程序的调试方法多文件程序的调试方法

操作步骤如下:操作步骤如下:① ① 在在 TC TC 环境下建立一个工程文件(环境下建立一个工程文件(扩展名为扩展名为 prjprj)) ,,设文件名为设文件名为 test.prjtest.prj ,文件内容如下:,文件内容如下:     file1.cfile1.c     file2.cfile2.c     fiile3.cfiile3.c若这3个文件不在当前盘或当前路径下,需要在文件若这3个文件不在当前盘或当前路径下,需要在文件名前加盘符和路径。如果已经将源文件编译成目标文名前加盘符和路径。如果已经将源文件编译成目标文件,可在工程文件中直接写目标文件名。件,可在工程文件中直接写目标文件名。

1. 1. 使用工程文件将多个文件连接成一个可使用工程文件将多个文件连接成一个可执行文件的方法执行文件的方法

例如:将例如:将 file1.cfile1.c 、、 file2.cfile2.c 和和 file3.cfile3.c合并成一个可执行文件合并成一个可执行文件。。

Page 73: 第 7 章  函数与预处理命令

2005 3年 月

737.9 7.9 多文件程序的调试方法(续)多文件程序的调试方法(续)② ② 在在 TCTC 主菜单的“主菜单的“ ProjectProject”” 下拉菜单中选下拉菜单中选择“择“ Project nameProject name””项,输入文件名项,输入文件名 test.prjtest.prj(文件名前也可以加盘符和路径)。(文件名前也可以加盘符和路径)。

③ ③ 在在 TCTC环境下,按环境下,按 Ctrl+F9Ctrl+F9 ,系统首先找到工,系统首先找到工程文件程文件 test.prjtest.prj ,然后将,然后将 file1.cfile1.c 、、 file2.cfile2.c 和和file3.cfile3.c 分别进行编译分别进行编译,若编译没通过,根据,若编译没通过,根据出错出错信息信息检查、修改源程序,然后再按检查、修改源程序,然后再按 Ctrl+F9Ctrl+F9 重新重新编译。若编译通过,系统自动生成三个对应的目编译。若编译通过,系统自动生成三个对应的目标文件:标文件: file1.obj file1.obj 、、 file2.obj file2.obj 和 和 fiile3.objfiile3.obj 。接。接着系统自动进行着系统自动进行连接连接,,连接连接成功则生成成功则生成 test.exetest.exe并自动执行该程序。并自动执行该程序。

Page 74: 第 7 章  函数与预处理命令

2005 3年 月

747.9 7.9 多文件程序的调试方法(续)多文件程序的调试方法(续)2. 2. 使用文件包含命令将多个文件连接成一使用文件包含命令将多个文件连接成一个可执行文件的方法个可执行文件的方法

以例 7.27 为例,分析文件包含后源程序的情况。

Page 75: 第 7 章  函数与预处理命令

2005 3年 月

757.9 7.9 多文件程序的调试方法(续)多文件程序的调试方法(续)

file3.cfile3.c

#include "file2.c"#include "file3.c"

file1.cfile1.c

A

file2.cfile2.c

B

C

B

C

A

file1.cfile1.c预处理前预处理前 预处理后预处理后

Page 76: 第 7 章  函数与预处理命令

2005 3年 月

76