C语言chap6(函数与宏定义).ppt_第1页
C语言chap6(函数与宏定义).ppt_第2页
C语言chap6(函数与宏定义).ppt_第3页
C语言chap6(函数与宏定义).ppt_第4页
C语言chap6(函数与宏定义).ppt_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

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

文档简介

2019/5/26,1,6.1 函数概念,第 6 章 函数与宏定义,在进行程序设计时,程序员总是将复杂的问题进行分解, 化整为零。编写一段小程序就能解决一个小问题,然后将这些小程序拼装起来就能解决非常大而复杂的问题。在 C 中,这些小程序统称为函数。,在 C 中,由系统提供的函数放在函数库中供用户选用,以减少重复编写程序模块的工作量。, 所有函数都是平行的,相互独立不能嵌套,但可以相 互调用(main函数不能被其它函数调用)。, C 函数分标准函数(库函数)和自定义函数。, C 函数本身分有参函数和无参函数。,2019/5/26,2,6.2 函数定义的一般形式,1无参函数的定义形式,类型标识符 函数名( ) 说明部分 ; 语句; ,无参函数一般不需要返回函数值,可以省略类型标识符。,2有参函数的定义形式,类型标识符 函数名(形式参数表) 形式参数说明; 说明部分 ; 语句; ,无分号!,无分号!,2019/5/26,3,3“ 空函数 ”,例: int max(x,y) int x, y; / 形式参数说明 / int z; / 函数体中的说明部分 / z=xy ? x : y; return(z); ,类型说明符 函数名( ) ,“空函数”什么操作也不做。其作用是在此处留一函数的位置,以便将来扩充功能之用。函数名也在将来换取实际的函数名。,这两行可以写成一行: int max(int x, int y),2019/5/26,4,6.3 函数参数和函数的值,1形式参数和实际参数,例6.1 从键盘输入两个数,输出其中较大的一个。 main( ) int a, b, c ; scanf(“%d,%d”, ,2019/5/26,5, 形参变量在被调用前不占用存储单元;在被调用结束后,形参所占存储单元亦被释放。 实参可以是常量、变量或表达式。 必须指定形参类型,且必须与实参的类型一致。 实参对形参的数据传递是“值传递”。即单向传递,不能逆传。 可以在形参表中直接说明形参类型。如: int max(int x, int y); float fun(int a10, int n);,注意:,2019/5/26,6, 函数的返回值是通过return语句获得的。当不需返回函数值时,可省去return语句。 return语句的后面可以有括号,也可以没有。如: return z ; return(z); return语句的后面可以是变量,也可以是表达式。如: return(x y ? x : y); return语句返回值的类型应与该函数的类型一致。否则以函数类型为准。,2函数的返回值,2019/5/26,7, 若函数中没有return语句,则该函数被调用后也会带回不确定的值。如:main( ), int a, b, c ; a=printstar( ); b=print_message( ); c=printstar( ); printf(“%d,%d,%dn”,a,b,c); ,输出的a, b, c的值将是随机值。, 为了明确表示不需要函数返回值,可以用“void”定义函数为“无类型”。此时,不得使用a=printstar( )之类的语句。,凡不需要返回值的函数,一般均定义为“void”类型。,2019/5/26,8,1函数调用的一般形式 函数名(实参表); 说明: 对于无参函数,尽管没有“实参表”,但也不得省略括号。 “实参表”中的参数之间用逗号分开。 实参与形参之间的个数及类型必须一一对应。 对实参求值的顺序是自左至右还是自右至左, 视具体的系统而定。Turbo C 和 MS C 是按自右至左的顺序求值。,6.4 函数的调用,2019/5/26,9,例6.2 main( ) int i =2 , p ; p = fun( i , + + i ); printf ( “%d”, p ) ; int fun ( a , b ) int a , b ; int c ; if ( a b ) c = 1; else if ( a = = b ) c = 0 ; else c = 1 ; return( c ) ; ,输出结果: 0,注意:这里是按自右至左求值的,相当于fun(3,3)。若按自左至右求值,则相当于fun(2,3), 则输出为-1。,2019/5/26,10,调用函数,可以有如下三种方式: 将函数调用作为一个语句。如: printf(“MS C pragramming”); gets(s); 将被调用的函数写在表达式中。如: c=2max(a, b); 注意:被调用函数max必须有确定的返回值。 将函数调用作为一个函数的实参。如: m=max(max(a, b), c); 3对被调用函数的说明 在一个函数中调用另一个函数时: 如果被调函数为库函数,则应在文件开头用“#include”命令声明相应的“头文件”。如: #include “stdio.h” #include “math.h”,2函数调用的方式,2019/5/26,11,类型标识符 被调函数名( ); 如:float add( ); 如果被调函数为自定义函数且其定义在主调函数定义之前,则在主调函数中可不必声明其类型。因为编译程序已知道其类型。 如果被调函数的值是整型或字符型,可不必声明类型,系统自动按整型说明。 如果在所有被调函数定义之前、在文件的开头、在函数的外部已对被调函数作了类型说明,则在各主调函数中可不必说明其类型。如: char letter( ); float f( ); int i( ); main( ) / 不必说明它所调用的函数的类型 /, 如果被调函数为自定义函数且其定义在主调函数定义之后,则应在主调函数中声明其类型(称之为”函数声明”)。其语句形式为:,2019/5/26,12,例6.3 编程求11999之间的数m,而m、m2和m3均是回文数(数位左右对称)。 如:m=11、m2=121、m3=1331, 。,#include “stdio.h” main( ) long int m; for (m=11; m1000; m+) if (fun(m) ,2019/5/26,13,C 语言函数的定义都是相互平行、独立的,不能嵌套定义。但可以嵌套调用函数。所谓函数的嵌套调用就是在被调用的函数中又调用另外的函数。 6.6 函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。,6.5 函数的嵌套调用,2019/5/26,14,例6.4 有5人排成一队,从最后一人开始,其年龄均比前面的人大2岁,而最前面的人年龄是10岁,问最后一人的年龄是多少岁?,Main( ),age(5),age(4)+2,age( ) n=5,age(3)+2,age( ) n=4,age(2)+2,age( ) n=3,age(1)+2,age( ) n=2,age(1) =10,age( ) n=1,age(1)=10,age(1)=12,age(1)=14,age(1)=16,age(1)=18,输出age(5),2019/5/26,15,函数的递归调用利用了堆栈技术。在本例中:,Age(5),Age(51)+2,Age(41)+2,Age(31)+2,Age(21)+2,10+2=12,12+2=14,14+2=16,16+2=18,18,Age(21)+2,Age(31)+2,Age(41)+2,Age(51)+2,Age(5),Age(5),Age(51)+2,Age(41)+2,Age(31)+2,Age(21)+2,入栈,出栈,出栈结果,写出下面程序的运行结果: 例6.4 e #include “stdio.h“ fun(int x) int p; if(x=0|x=1) return 3; p=x-fun(x-2); return p; void main() printf(“%dn“,fun(9); ,结果为:7,2019/5/26,17,汉诺塔问题参考程序(将A座上的N个disks 借助B座移动到C座),#include “stdio.h“ void h(int m,char x,char y,char z); void main() int n; char a=a;char b=b;char c=c; printf(“input disks:“); scanf(“%d“, ,2019/5/26,18,1局部变量 在一个函数内部定义的变量,只能在本函数内使用和有效,称为“局部变量”。 主函数main中定义的变量,也只能在主函数内使用和有效。 不同函数中可以使用相同名字的变量,且互不干扰。 形式参数也是局部变量,也只能在所在函数内使用和有效。 可以在一个函数内的复合语句中定义变量,且这些变量只在本复合语句中有效。 这种复合语句也称为“分程序”或“子模块”。 2全局变量 在函数(包括main函数)外定义的变量为外部变量,称为“全局变量”。全局变量的有效范围为从定义变量的位置开始到本源文件结束。,6.7 局部变量和全局变量,2019/5/26,19, 如无必要,不要使用全局变量。因为全局变量既降低程序的清晰性和函数的通用性,且又在程序的全部执行过程中都占用存储空间。 在文件开头定义的外部变量才可在整个文件范围内使用,若在定义点之前的函数需引用外部变量,则可用关键字“extern”作“外部变量说明”。 注意:外部变量定义和外部变量说明并不是同一回事。外部变量的定义只能有一次,它的位置在所有函数之外。而同一程序中的外部变量说明可以有多次,它的位置在函数之内(哪个函数要用就在哪个函数中说明)。系统根据外部变量的定义(而不是根据外部变量的说明)分配存储单元。对外部变量的初始化只能在“定义”时进行,“extern”只是申明该变量是一个已在外部定义过的变量而已。 如果在同一源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。, 全局变量增加了函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,当需要从一个函数中带回多个值时,就能克服函数调用只能返回一个值的局限性。,2019/5/26,20,输出结果: 10, 4, 8 10, 2, 5,例6.5 #include “stdio.h” main( ) int a=1, b=2, c=3; a+; c+=b; int b=4, c; c=2b; a+=c; printf(“%d,%d,%dn”, a, b, c); printf(“%d,%d,%dn”, a, b, c); ,C编译內存管理示意图:,程序区,静态存储区,用户定义存储区,动态存储区,数据区,2019/5/26,22,1变量的存储类别 静态变量在程序的运行期间占用固定的存储空间,直到程序的终止而释放;而动态变量是在程序的运行期间随着函数的调用随时动态地占用和释放存储空间。即存储方式是根据变量的存储类别决定的。 C的存储类别有四种: 自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。 2局部变量的存储方式 函数中的局部变量若未专门说明,都是由编译系统自动动态分配存储空间,这类局部变量称为自动变量,其类型说明前不论是否有关键字“auto”,都属于动态存储类别。,6.8 动态存储变量与静态存储变量,2019/5/26,23, 局部静态变量在静态存储区分配存储单元,在程序的整个运行期间都不释放。而动态变量在动态存储区分配存储单元,函数调用结束后即释放。 局部静态变量在编译时只赋初值一次,以后每次被调用时不再重新赋值而只保留前次被调用结束时的值。动态变量赋初值不是在编译时而是在函数被调用时进行的,函数每次被调用时重新赋值,相当于赋值语句。 静态变量若未赋初值,则在编译时自动赋初值0 (数值型)或空字符(字符型)。而动态变量若未赋初值,则其值是不确定的。 只有将数组定义为全局变量或静态变量时才能赋初值。但应注意,如果数组的值在函数被调用过程中改变了,则影响下一次调用时的初值。 虽然局部静态变量在函数被调用结束后其值仍然存在,但其它函数是不能引用的。,2) 若希望被调函数在结束后,其局部变量占用的存储空间不释放,以便保留其变量的值,用于下次调用该函数,则用“static”说明为 “局部静态变量”。,2019/5/26,24,3全局变量的存储方式 在文件开头用extern作说明,可以引用另一个文件中定义的全局变量。但用static说明的全局变量不能被其它文件引用。,例6.6 打印1到5的阶乘值。 int fac(n) int n; static int f =1; / 定义f 为int型静态变量 / f = f n ; return( f ); main( ) int i; for (i=1; i=5; i+) printf(“%d!=%dn”, i, fac(i); ,2019/5/26,25,就函数的本质而言都是全局的。但根据其是否能被其它文件所调用,将它们区分为内部函数和外部函数。 1内部函数 如果一个函数只能被本文件的其它函数所调用,则将它称为内部函数。可以用“static”将函数定义为内部函数。如: static int fun(a, b); 同静态变量一样,内部函数只局限于本文件内使用,因而不会和其它文件中的同名函数发生冲突。 2外部函数 在定义函数时,如果在其类型说明前冠以extern,则表示此函数为外部函数。如: extern int fun(a, b); 外部函数可以被其它文件所使用。如果省略extern,则隐含该函数为外部函数。,6.9 内部函数和外部函数,2019/5/26,26,存储类别,总结,函数内,函数外,作用域,存在性,作用域,存在性,auto ,register ,static 局部 ,static 外部 , ,不加 static 全局(外部),(只限本文件),2019/5/26,27,“编译预处理”是C与其它高级语言的一个重要区别。它们是:宏定义;文件包含;条件编译。 1. 宏定义 1) 不带参数的宏定义 用指定的标识符(宏名)代表一个常量或字符串。 #define 标识符 常量/字符串,6.10 编译预处理,2019/5/26,28,例6.7 求圆的周长、面积和圆球的体积。 #define PI 3.1415926 / 宏名PI用大写,行末无分号 / main( ) float l, s, r, v ; scanf(“%f ”, ,2019/5/26,29, 进行宏定义时,可以引用已定义的宏名,层层置换。 对程序中用双引号括起来的字符串,即使与宏名相同,也不会被置换。, 宏名的有效范围为该宏定义命令之后至本源文件结束。通常,宏定义命令一般写在文件开头或函数之前作为该文件的一部分。, 可以用 #undef 命令终止宏定义的作用域。如: #define G 9.8 main( ) #undef G float f( ) ,G 的作用范围,2019/5/26,30,2带参数的宏定义,语句形式: #define 宏名(形参表) 表达式,例6.8 #define PI 3.1415926 #define S(r) PI r r main( ) float a, area; a=3.6; area=S(a); / area=3.1415926aa,但不会置换a为3.6 / printf(“r=%f narea=%f n”,a,area); ,2019/5/26,31, 若将area=S(a) 改写成 area=S(a+2),将置换为: area=PI a+2a+2,而不是: area=PI (a+2)(a+2)。 若要达到后者之目的,则应将宏定义命令改写为: #define S(r) PI (r) (r) 宏名与括号之间不得有空格,因为宏名与表达式之间的分隔符为空格。如果用#define S (r) PI r r 的话,则被置换为 area=(r) PI r r(a) 宏展开并不进行值的传递,即不求表达式的值,也没有“返回值”的概念。 宏不存在类型问题,宏名无类型,参数也无类型,表达式可以是任何类型。, 对带参数的宏定义的置换展开是用“表达式”对等的置换“形参表”中的参数。上例中的“”是不会被置换的。,2019/5/26,32,注:这里的 t 展开后为 t = (a+b)(c+d)?(a+b):(c+d) 如果第一行写成: #define MAX(x,y) xy ? x : y 则这里的 t 展开后为 t = a+bc+d?a+b:c+d 因为置换展开是用“表达式”对等的置换“形参表”中的参数。, 定义带参数的宏,可以实现一些简单的函数功能。,如: #define MAX(x,y) (x)(y)?(x) : (y) main( ) int a, b, c, t ; t = MAX(a+b, c+d); ,2019/5/26,33,例6.9(演示) #include “stdio.h” #define N 2 #define M N+1 #define NUM (M+1)M/2 main( ) int i, n=0; for (i=1; i=NUM; i+) n+; printf(“%2d”, n); printf(“n”); ,输出结果: 1 2 3 4 5 6 7 8,NUM被代换展开后为:( 2+1+1) 2+1/2,2019/5/26,34, 如果“文件1”包含“文件2”,而“文件2”又包含“文件3”,则可在“文件1”中使用两个include命令。即: 文件file1.c #include“file3.h” / file3.h应出现在file2.h之前 / #include“file2.h” 文件包含可以嵌套。即一个被包含文件中又可以包含另一个文件。如 : 文件file1.c #include“file2.h” 文件file2.c #include“file3.h” ,6.11 “文件包含” 处理 在C中可以用 #include 命令实现一个文件包含另一个文件。 一个include命令只能指定一个被包含文件,如果要包含n个文件,则要用n个include命令。语句形式: #include “文件名”,2019/5/26,35,6.12 条件编译 条件编译的语句形式: 1#ifdef 标识符 程序段1 #else 程序段2 #endif 其作用是:如果“标识符”已定义,则编译“程序段1”,否则编译“程序段2” 。, 在#include命令中,文件名既可用双引“ ”号也可用尖括号括起来。但用双引号更方便系统查找被包含的文件。,2019/5/26,36, 其中

温馨提示

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

评论

0/150

提交评论