大学C语言程序设计二版(电子工业)6_第1页
大学C语言程序设计二版(电子工业)6_第2页
大学C语言程序设计二版(电子工业)6_第3页
大学C语言程序设计二版(电子工业)6_第4页
大学C语言程序设计二版(电子工业)6_第5页
已阅读5页,还剩85页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第6章 函数与编译预处理6.1 函数概述6.2函数的定义和说明6.3函数的调用6.4函数的参数6.6变量的存储类型6.7内部函数和外部函数6.8编译预处理命令6.5函数的嵌套调用与递归调用 6.1 函数概述函数是封装了一定功能的模块C语言是函数驱动式语言(一个源程序文件由一个或多个函数组成)#include “stdio.h” void main( )/函数头 int a=8; printf( “a=%d”,a); /函数体一个C源程序必须有且只能有一个主函数main()。C程序总是从main()函数开始执行,最后结束于main()函数,其他函数是通过main()直接或间接调用完成其功能的。#

2、includeint add(int x, int y) int s; s=x+y; return s; main( ) int a=1,b=2,c; c=add(a,b); printf(%dn,c); kj61一、函数定义二、函数声明三、函数调用#includemain( ) int a=1,b=2,c; int add(int x, int y); c=add(a,b); printf(%dn,c);int add(int x, int y) int s; s=x+y; return s; 函数概述(1)一个C程序由一个或多个源(程序)文件组成可分别编写、编译和调试。(2)一个源文件由一

3、个或多个函数组成,可为多个C程序公用。(3)C语言是以源文件为单位而不以函数为单位进行编译的。(4)在C语言中,所有函数(包括主函数main())都是平行的。一个函数的定义,可以放在程序中的任意位置,主函数main()之前或之后。但在一个函数的函数体内,只能调用其他函数,不能再定义另一个函数,即不能嵌套定义。(5)主函数名main是系统定义的,是运行时首先被调用的函数,它可以调用其他函数,但不能被其他函数调用;其他函数间可以互相调用,也允许嵌套调用。(6)从函数定义的角度看,函数可分为库函数和用户定义函数两种。 库函数:由C系统提供,用户无须定义,也不必在程序中进行类型说明,只需在程序前写出包

4、含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到的printf()、scanf()、getchar()、putchar()、gets()、puts()等函数均在头文件stdio.h中,sqrt(), fabs(),pow()等函数均在头文件math.h中。 用户定义函数:由用户按需要编写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且还必须在主调函数模块中对该被调函数进行类型说明,然后才能使用。(7)从主调函数和被调函数之间数据传送角度看,函数又可分为无参函数和有参函数两种。(8)C语言的函数兼有其他语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返

5、回值函数和无返回值函数两种。 6.2.1 函数定义语言函数定义格式如下:函数返回值的类型名 函数名(类型名 形式参数1,类型名 形式参数2,)/*函数头*/ 说明部分; /*函数体*/ 语句部分; 其中 内为可选项。注意:函数名、一对圆括号和花括号不能省!6.2 函数的定义和说明1.无参函数无参函数的一般形式 函数返回值的类型名 函数名( void ) 说明语句部分; 可执行语句部分; 例 : 构造一个输出一行”*”的函数:void printstar(void) printf(“*n”); kj622.有参函数例:编写函数交换个数;kj63 void swap (int a,int b) i

6、nt temp; temp=a; a=b; b=temp; 不能写成: void swap(int a, b) int temp; temp=a; a=b; b=temp; 函数返回值类型函数名参数类型说明及参数列表6.2.2 函数的返回值函数分为有返回值函数和无返回值函数。return 语句的格式:return(表达式); 或 return 表达式; 或 return;功能:1、把return后面“表达式”的值带给调用函数; 2、把控制转向调用函数; 格式 return;只有功能2例 :编写函数,返回2个整数的最大公约数。(辗转相 除法) kj64 int f(int x,int y) in

7、t r,t; if(xy) t=x;x=y;y=t; while(r=x%y)!=0) x=y; y=r; return y; 注意:(1) 声明为void型的函数中不能包括带值的return语句;主函数体内一般不用return语句。(2)当函数没有return语句时,以结束函数的大括号 作为返回点。但这时并不表明函数没有返回值,这时的返回值是系统给的不确定值。(3)在同一函数内,可根据需要在多处出现return语句,但函数第一次遇到return时就立即停止执行,并返回到主调函数;(4)return语句中返回值表达式的类型应该和函数类型一致。如果不一致,则以函数类型为准,由系统自动进行转换。如

8、果缺省函数类型,则系统一律按int类型处理。 int f(char s1,char s2) if(s1=s2) return 1; return 0; kj65 例 : 计算两数之和 #includedouble add(double x,double y) double s; s=x+y; return s; main() double a,b,c; a=10.5;b=20.3; c=add(a,b); printf(%lf,add(a,b); add(a,b);Kj66 运行结果为:30.8改成:int add(double x,double y) ?6.2.3 函数说明1函数说明的形式

9、函数说明也称为函数声明,使用函数说明语句能够让C编译程序了解函数返回值类型 被调用函数说明一般格式如下:函数类型 函数名(数据类型 参数名1, 数据类型 参数名2); 函数说明语句其实就是函数定义中的函数首部加上分号,这些内容称为函数原型。如: double max(double x,double y); void swap(int a,int b); 例 : 计算两数之和 #includemain() double a,b,c; a=10.5;b=20.3; c=add(a,b); printf(%lf,add(a,b); add(a,b);double add(double x,doubl

10、e y) double s; s=x+y; return s; kj67double add(double x,double y);double add(double x,double y);注意:有三种情况不需说明: a、函数返回值为整型或字符型; b、被调用函数定义出现在主调函数之前; c、已在所有的函数定义之前(即文件的开头)说明了函数类型。 6.3 函数的调用1、调用形式: 函数名(实参表列); 实参与形参个数相等,类型一致。调用函数时两者按顺序一一对应传递数据。main() int a=1,b,f(int,int); b=f(a,+a); printf(%d,b);int f(int

11、 x, int y) int z; if(xy) z=1; else if(x=y) z=0; else z=-1; return(z);例: 参数求值顺序main() int a=1,b,f(int,int); b=f(a, a+); printf(%d,b);int f(int x, int y) int z; if(xy) z=1; else if(x=y) z=0; else z=-1; return(z);运行结果:0运行结果:12、调用方式: 按函数在程序中出现的位置来分有三种方式。 函数语句。 只完成一个操作,并不要求函数带回值。 如:printstar(); scanf(“%d

12、”,&a); 函数表达式。 出现在表达式中,函数值参与表达式运算。 如: c=2*max(a,b); y=x+power(x,3); 函数参数。 函数调用作为一个函数的实参。 如: m=max(a,max(b,c); printf(“%8.2fn”,power(x,3);课堂练习:下面程序的输出结果是( )。#includefunc(int a, int b) int c; c=a+b; return c;main() int x=6,y=7,z=8,r; r=func(x-,y+=2,x+y),z); printf(dn,r); A11B20C21 D22 课堂练习:若有以下函数调用语句:

13、fun(a+b,(x,y),fun(n+k,d,(a,b);fun函数参数的个数是A) 3 B)4 C)5 D)66.4 函数的参数形参与实参形式参数:定义函数时函数名后面括号中的变量名。实际参数:调用函数时函数名后面括号中的表达式。例 :编写程序实现从两整数中求较大数。#includefloat max(float x,float y);/*函数说明*/main() float a,b; float c; scanf(f,f,&a,&b); c=max(a,b);/*调用函数语句,a,b为实参*/ printf(a=f,b=fnmax=fn,a,b,c);float max(float x,

14、float y)/*函数定义,x,y为形参*/ float z; z=xy?x:y; printf(“x=f,y=fnz=fn”,x,y,z); return(z);x y形参a b实参说明:实参必须有确定的值,形参必须指定类型形参与实参类型一致,个数相同若形参与实参类型不一致,自动按形参类型转换函数调用转换值传递方式 形参在函数被调用前不占内存;函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值 特点:形参与实参占用不同的内存单元单向传递例: 计算x的立方#include float cube(float x) return(x*x*x)

15、;main() float a, product; scanf(%f,&a); product=cube(a); printf(” %.4f %.4fn,a,product);xaproduct1.21.21.7281030 x:y:调用前:调用结束:1030 x:y:例 : 交换两个数#include main() int x=10,y=30; printf(%d,%dn,x,y); printf(swapped:n); swap(x,y); printf(%d,%dn,x,y);swap(int a,int b) int temp; temp=a; a=b; b=temp;调用:1030a

16、:b:1030 x:y:swap:1030 x:y:3010a:b:temp一般函数调用main( )调用函数a结束a函数执行2134566.5 函数的嵌套调用和递归调用6.5.1 函数的嵌套调用 函数的嵌套调用是指,在执行被调用函数时,被调用函数又调用了其它函数。这与其它语言的子程序嵌套调用的情形是类似的,其关系可表示如右图所示。 例: 求三个数中最大数和最小数的差值 int max(int x,int y,int z) /*定义函数max 求x,y,z中最大值*/ int r; r=xy?x:y; return(rz?r:z);int min(int x,int y,int z) /*定义

17、函数min,求x,y,z中最小值*/ int r; r=xy?x:y; return(rz?r:z);int dif(int x,int y,int z) /*定义函数dif*/ return max(x,y,z)-min(x,y,z); /*嵌套调用*/void main() int a,b,c,d; scanf(%d%d%d,&a,&b,&c); d=dif(a,b,c); /*调用函数dif*/ printf(Max-Min=dn,d);若输入2、3,求以下程序的运行结果,分析嵌套调用的执行过程。#include int f (int n) int rtn=0; int i; for(i

18、=1;i0程序段为:#include main() int m=1,i,n; scanf(%d,&n) ; for(i=1;i0递归函数一般形式:函数名f(参数) if(n=初值) 结果=.; else 结果=含f(n-1)的表达式; return (返回结果);#include int f(int x) int t; if(x=0) t=1;/*递归出口*/ else t=f (x-1)*x; ;/*递归公式*/ return(t);main() int n=0,s; scanf(%d,&n); s=f(n); printf(%d! =%10d,n,s);f函数在回推阶段被调用了4次,递归调

19、用了3次,到终止条件才有确定的值,然后再递推出每一次调用的值,最后得到所求的结果。例 : 求Fibonacci数列:打印1,1,2,3,5,8,的前40个数。#include main( ) long f1, f2 ; int i ; f1=1 ; f2=1 ; for( i=1; i20 ; i+ ) printf(%12ld%12ld ,f1, f2 ) ; if( i%2=0) printf( n ) ; f1= f1 + f2 ; f2= f2 + f1 ; 方法1:用循环结构来实现#include int fib(int n) int f;if (n=1|n=2) f=1;else

20、f=fib(n-1)+fib(n-2);return f;main( ) int i; for( i=1; i=40 ; i+ ) printf(%15d,fib(i); if(i%4=0) printf(n); 方法2:用递归函数来实现例: 反向输出一个整数#includemain() int n;scanf(%d,&n);if(n0)n=-n;printf(-);while(n!=0)printf(%d,n%10); n=n/10;方法1:用循环结构来实现#includevoid main() void printn(int x); int n; scanf(d,&n); if(n=0&x

21、=9) /*若x为一位整数*/ printf(d,x); /*则输出整数x*/ else /*否则*/ printf(d,x10); /*输出x的个位数字*/printn(x/10); /*将x中的个位数字去掉,形成新的x后,继续递归操作*/ 方法2:用递归函数来实现输出一个正整数的递归算法为:if(n为一位整数) 输出n;else输出n的个位数字;对剩余数字组成的新整数重复反向输出操作; 例 :汉诺塔问题。 初始状态: 有3个塔,每个都堆放若干个盘子。开始时,所有盘子均在塔a上,并且,盘子从上到下,按直径增大的次序放置 。 要求解决问题:设计一个盘子移动的序列,使得塔a上的所有盘子借助于塔b

22、移动到塔c上。 两个限制:1)一次只能移动一个盘子; 2)任何时候都不能把盘子放在比它小 的盘子的上面。 假设塔a有n个盘子已知条件:若只有一个盘子,则是直接从移到 (递归出口)算法设计如下:第一步:把n - 1个盘子依照题目中的规则从塔a(源塔)借助于塔c(中间塔)搬到塔b(目标塔)。第二步:将剩下的一只盘(也就是最大的一只)直接从塔a(源塔)搬到那个仍然空着的塔c(目标塔)。第三步:再次将b塔(源塔)上的n - 1个盘子借助于塔a(中间塔)搬到塔c(目标塔)。这一步是没有问题的,因为c塔上仅有一只最大的盘。函数hanoi(int n,int a,int b,int c)设计:如果(n=1)

23、 则ac; 否则执行步2-1 调用函数hanoi(n-1,a,c,b); 2-2 ac; 2-3 调用函数hanoi(n-1,b,a,c);例 Hanoi问题#includevoid hanoi(int n,int a,int b,int c) if(n=1) printf(%d-%dn,a,c); else hanoi(n-1,a,c,b);printf(%d-%dn,a,c);hanoi(n-1,b,a,c); main() int n; printf(Input n:); scanf(%d,&n); hanoi(n,1,2,3); 运行情况如下: input n:3 the step:

24、1-3 1-2 3-2 1-3 2-1 2-3 1-3先看一个例子,错在那里?:#include stdio.hvoid f1( ) int t=2; a *= t; b /= t; main() int a, b; scanf(%d,%d, &a, &b); f1( ); / 调用函数f1( ) printf (a=%d,b=%d, a, b); 1编译程序会提示出错: Undefined symbol a 和 Undefined symbol b 。为什么?6.6变量的存储类型6.6.1变量的生存期和作用域生存期:变量在某一时刻存在-静态变量与动态变量作用域:变量在某区域内有效-局部变量与

25、全局变量(文件、函数、复合语句)6.6.2 变量的存储类型(特性)1.c语言中每一个变量有两个属性:数据类型,存储类型。完整的变量定义: 存储类型 数据类型 变量名;2.变量的存储类型(特性) 自动型 auto 静态型 static 寄存器型 register 外部型 extern程序区静态存储区动态存储区2.静态存储类型的变量的生存期为程序执行的整个过程,在该过程中占有固定的存储空间,称它们为永久存储。 static extern动态存储类型变量只生存在某一段时间内。例如,函数的形参和函数体内定义的变量,只是在程序进入该函数时才分配存储空间,当该函数执行完后,变量对应的存储空间又被撤销了。

26、atuo register局部变量与全局变量局部变量-内部变量定义:在函数内或复合语句定义说明:main(函数)中定义的变量只在main(函数)中有效。复合语句中定义的变量只在复合语句中有效。不同函数中同名变量,占不同内存单元。形参属于局部变量。局部变量可用存储类型:auto register。 static (默认为auto)float f1(int a) int b,c; .char f2(int x,int y) int i,j; main() int m,n; .a,b,c有效x,y,i,j有效m,n有效#includesub()int a,b; a=6; b=7; printf(su

27、b:a=%d,b=%dn,a,b); main() auto int a,b; a=3; b=4; sub(); printf(main:a=%d,b=%dn,a,b); 3自动变量局部静态变量值具有可继承性局部静态变量的初始化只在编译时进行一次,每次调用他们所在的函数时,不再重新赋初值;如定义但不初始化,自动赋或静态局部变量static 数据类型变量名 static int i=1;#includemain() void in (void); in (); in(); in();void in(void) int x=0; x+; printf(“%dn”,x); 4运行结果:1 1 1#i

28、ncludemain() void in (void); in (); in (); in ();void in (void) static int x=0; x+; printf(“%dn”,x); 5 运行结果:1 2 3静态局部变量例:分析执行结果f(int a)int b=0; static int c=3;b+;c+;printf(%5d%5d%5dn,a,b,c);return(a+b+c);main()int a=2,k;for(k=0;k3;k+)printf(%5dn,f(a); 6静态变量只初始化一次结果:2 1 4 (a,b,c) 7 (f(a)2 1 5 82 1 6

29、9(3)register(寄存器)型将使用频率高的变量定义为register型, 可以提高运行速度。 寄存器变量只限于整型、字符型、指针型的局部变量。 寄存器变量是动态变量,而且数目有限, 一般仅允许说明两个寄存器变量,过多的寄存器变量的编译器可能把它看成Auto类型。例如: register int d; register char c;全局变量-外部变量定义:在函数外定义,可为本文件所有函数共用有效范围:从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件外部变量说明:外部变量定义与外部变量说明不同若外部变量与局部变量同名,则外部变量被屏蔽外部变量可用存储类型:缺省 或 s

30、tatic#include extern int a, b; void swap( ) int t; t = a; a = b; b = t; printf(“swap:a=%d,b=%dn”,a,b); int a, b; main() printf(“ Enter a,b:”); scanf(“%d,%d”, &a, &b); swap( ); printf (“main:a=%d,b=%d”,a,b); 7Enter a,b:5,3 swap:a=3,b=5main:a=3,b=5运行结果全局变量有两个问题:其一:当全局变量定义在后,引用它的函数在前时,如何使用该全局变量?这就需要把该全

31、局变量的作用域延伸至该函数;其二:能否使在某文件中定义的全局变量,在其它文件中无须再次定义而直接使用它呢?这就需要把全局变量的作用域进行延伸。C语言可通过外部变量说明达到此目的。 定义 说明 次数: 只能1次 可说明多次位置: 所有函数之外 函数内或函数外分配内存:分配内存,可初始化 不分配内存,不可初始化外部变量说明: extern 数据类型 变量表;外部变量定义与外部变量说明不同若外部变量与局部变量同名,则外部变量被屏蔽例 外部变量定义与说明运行结果:max=13#includeextern int a,b; /外部变量说明int max(int x, int y); /函数说明main(

32、) printf(max=%d,max(a,b) ); int a=13,b=-8; /外部变量定义int max(int x, int y) /函数定义 int z; z=xy?x:y; return(z); #include int a=7,b=9; /a,b为全局变量 void f1(int x ) int t1 ; t1= b * x; printf(f1:t1=%d,a=%d,b=%dn, t1,3*a,3*b); main( ) f1(2); / 调用函数f1( ) printf (main: a=%d,b=%dn, a, b); t8#include int a,b; /*a,b

33、为全局变量*/ void f1(int x ) int t1,a; /重新定义a, 没重新定义b, a值传不过去。 a= x* 4; t1= b * 3; b = 10; printf(f1:t1=%d,a=%d,b=%dn, t1,a,b); main( ) a=2; b=4; /* 此a,b是全局变量,赋值 */ f1( 2); /* 调用函数f1( ) */ printf (main: a=%d,b=%d, a, b); 9f1:t1=12,a=8,b=10main:a=2,b=10结论:全局变量与局部变量同名时,局部变量起作用,全局变量被屏蔽(不影响)变量存储类型小结静态动态存储方式程

34、序整个运行期间函数调用开始至结束生存期编译时赋初值,只赋一次每次函数调用时赋初值自动赋初值0或空字符不确定未赋初值静态存储区动态区存储区寄存器局部变量外部变量作用域定义变量的函数或复合语句内本文件其它文件局部变量默认为auto型register型变量个数受限,且不能为long, double, float型局部static变量具有全局寿命和局部可见性局部static变量具有可继承性extern不是变量定义,可扩展外部变量作用域register局部staticauto外部static外部存储类别6.7 内部函数、外部函数 语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。6

35、.7.1 内部函数(又称静态函数) 如果一个函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,则称为内部函数。定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,定义格式如下: static 函数类型 函数名(函数参数表) 如 static int fun(a,b,c) 6.7.1 外部函数在定义函数时,如果没有加关键字“static”,或者冠以“extern” , 则表示此函数是外部函数,其定义格式为: extern 函数类型 函数名(函数参数表) 调用外部函数时,必须对被调用的外部函数进行说明。例/*FILE1*/#includeint i; /*在F

36、ILE1中定义外部变量i*/void func(); /*外部函数说明*/main()i=5;printf(file1:%dn,i);func();/*FILE2*/extern int i;/*外部变量说明,FILE2引用FILE1中定义的外部变量i*/void func()printf(file2:%dn,i);该程序的运行结果是:file1:5 file2:56.8编译预处理 “编译预处理”是C语言编译系统的 一个组成部分。是在编译前由编译系统中的预处理程序对源程序的预处理命令进行加工。 源程序中的预处理命令均以“#”开头,结束不加分号,以区别源程序中的语句,它们可以写在程序中的任何位置

37、,作用域是自出现点到源程序的末尾。 预处理命令包括执行宏定义(宏替换)、包含文件和条件编译。一.宏定义 简单宏定义1.一般形式为: #define 宏名 串 (宏体) 如: #define PI 3.14159 /*定义后,可以用PI来代替串3.14159*/2.宏定义的作用 在宏定义之后, 该程序中宏名就代表了该字符串。说明:占单独书写行语句尾不加分号3.说明可以用 #undef命令终止宏定义的作用域。例如:#undef PI宏定义的嵌套使用# define R 3.0# define PI 3.1415926# define L 2*PI*R /*宏体是表达式*/# define S PI

38、*R*Rmain ( ) printf (L=%fnS=%fn, 2*PI*R , PI*R*R); /*2*PI*R替换L, PI*R*R替换S */ t6-1程序运行结果如下:L=18.849556S=28.274333双引号内与宏同名的字母不作宏展开.main ( ) printf (L=%fnS=%fn,L,S); t6-1#define X 5#define Y X+1#define Z Y*X/2main ( )int a=Y;printf(%dn,Z);printf(%dn,-a); t6-2 / Z=Y*X/2=5+1*5/2=7运行结果:7 5带参数的宏定义1.带参数的宏定义

39、的一般形式为# define 宏名(参数表) 字符串#define S(a,b) (a)*(b) 2.带实参的宏名被展开 宏名被所定义的宏体替换, 宏体中的形参按从左到右的顺序被实参替换。例如: 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) x*x x=4; y=6; z=POWER(x+y);宏展开:z=x+y*x+y;结果为:100结果为:34课堂练习:#

40、include#define MIN(x,y) (x)(y)? (x):(y)main() int i,j,k; i=10;j=15;k=10*MIN( i,j ); printf(%dn,k);A)15 B)100 C)10 D)150/ 10*ij? 10*10(y)?(x):(y) .main() int a,b,c,d,t; . t=MAX(a+b,c+d); t6-4宏展开:t=(a+b)(c+d)?(a+b):(c+d);例 用宏定义和函数实现同样的功能int max(int x,int y) return(xy?x:y);main() int a,b,c,d,t; . t=max(a+b,c+d); 带参的宏与函数区别带参宏函数处理过程不分配内存简单的字符置换分配内存先求实参值,再代入形参处理时间编译时程序运行时参数类型无类型问题定义实参,形参类型

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论