《C语言大学教程 教学课件 PPT 作者 王浩鸣 郭晔 第6章 函数》由会员分享,可在线阅读,更多相关《C语言大学教程 教学课件 PPT 作者 王浩鸣 郭晔 第6章 函数(37页珍藏版)》请在金锄头文库上搜索。
1、第6章 函数,6.1 函数定义,6.1.1函数概念 根据模块化程序设计的原则,一个较大的程序一般要分为若干个小模块,每个模块实现部分特定的功能。在C语言中,模块是通过对函数的调用来实现特定功能的。所以程序的全部工作可以理解为由各式各样的函数实现的,即函数式语言,(1)函数是按规定格式书写的能完成特定功能的一段程序。 (2)C语言是以源文件为单位进行编译的,一个源程序文件由一个或多个函数组成。 (3)一个C程序由一个多个源文件组成,C语言将源文件分别编译成目标文件,然后将这些目标文件链接在一起,形成一个可执行文件。 (4)从用户使用的角度看,函数有两种: 标准函数:即库函数,由系统提供,用户可以
2、直接使用。 用户自定义函数:用户根据自己的需要而编写的。,函数之间的调用,(5)在C语言中,程序总是从主函数开始执行,最终在主函数中结束,不用考虑主函数在程序中的位置,它可以放在程序的任何位置。 (6)从函数被调用时是否需要参数来看,函数可分为: 无参函数:一般用来执行指定的操作。调用这类函数时,主调用函数并不向被调用函数传递参数。无参函数对是否带回函数值并没有特别的规定。 有参函数:在主调用函数(简称为调用函数)和被调用函数(简称为被调函数)之间有参数进行传递。调用函数可以将数据传递给被调函数使用,被调函数中的数据也可以返回给调用函数使用。,6.1.2函数定义,1. 无参函数的定义形式 类型
3、标识符 函数名() 声明与定义部分 语句; ,2. 有参函数定义的一般形式 类型标识符 函数名(形式参数表列) 声明与定义部分 语句; ,3、“空函数”的定义 类型标识符 函数名() ,6.2 函数调用,6.2.1函数调用过程 所谓函数的调用,是指一个函数(调用函数)暂时中断本函数的运行,转去执行另一个函数(被调函数)的过程。被调函数执行完后,返回到调用函数中断处继续执行调用函数剩余指令的过程。被调函数执行完毕后一定要返回调用函数,在函数的调用和返回两个过程中,两个函数之间发生数据的交换,6.2.2函数调用形式,在程序中,调用函数执行函数体,其过程与其它语言的子程序调用相类似。语言中,函数调用
4、的一般形式为: 函数名(实际参数表列),6.2.3函数的形式参数和实际参数,函数的形参和实参具有以下特点: (1)形参变量只有在函数被调用时才会分配内存单元,在调用结束时,立刻释放所分配的内存单元。因此,形参只有在函数内部有效,函数调用结束返回主调函数后则不能再使用该形参变量。 (2)实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参,因此应采用赋值,使用键盘输入等办法使实参获得具体的确定值。 (3)实参和形参在数量、类型和顺序上应严格一致,否则在编译时会发生类型不匹配的错误。 (4)函数在调用中进行的数据传送是单向的
5、。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。,6.2.4 参数传递方式,1. 值传递 在函数调用时,实参将其具体的值传递给形参,值传递属于单向传递,只能由实参传递给形参,而不能由形参传回给实参。即形参在被调函数中的任何改变将不会影响调用函数中实参的值。,2地址传递 地址传递指的是调用函数时,实参将某些变量、字符串、或数组等的地址传递给形参。这样实参和形参指向同一个内存空间,在执行被调用函数的过程中,对形参所指向空间中内容的变化,能够直接影响到调用函数中对应的量。 在地址传递方式下,形参和实参可以是指针变量或数组名
6、,实参还可以是变量的地址,6.2.5函数结果返回,函数结果是指函数被调用之后,执行函数体中的程序段所得的,并最终返回给调用函数的值,即函数返回值,(1)函数的返回值是通过函数中的return语句传递出去的。return语句将被调函数中的一个计算结果带到调用函数中。return语句的一般形式为: return 表达式; 或 return (表达式); (2)如果不需要从被调用函数返回函数值,可以不使用return语句。 (3)一个函数中可以有一个以上的return语句。 (4)return语句后面的括号可以省略,如return z;它与return(z)等价,(5)return后面的值可以是一个
7、表达式。 (6)既然函数有返回值,这个值就应属于某个确定的类型,应在定义函数时指定该函数值的类型。 (7)如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。即函数类型决定返回值的类型。 (8)为了明确表示“不带回值”,可以使用“void”定义“无类型或空类型”。这样,系统保证不使用函数带回任何值,6.2.6函数的嵌套调用,6.2.7函数的声明,主调函数中调用某函数之前应对该被调函数进行说明(声明),这与使用变量之前要先进行变量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理,一般形式为:
8、类型说明符 被调函数名(类型 形参,类型 形参); 或 类型说明符 被调函数名(类型,类型);,6.3 递归函数,6.3.1 递归函数基本概念 所谓递归函数是指在它的函数体内调用自身,即在递归调用中,主调函数也是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,6.4变量与函数,6.4.1局部变量和全局变量 根据按作用域范围不同,变量可分为两种类型,即局部变量和全局变量,1. 局部变量,局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内,离开该函数后再使用这种变量是非法的。关于局部变量的作用域还要说明以下几点: (1)主函数中定义的变量也只在主函数中使
9、用,不能在其它函数中使用。 (2)主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。 (3)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。 (4)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。 (5)在复合语句中也可定义变量,其作用域只在复合语句范围内,2全局变量,全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为ext
10、ern。但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明,6.4.2变量生命周期和静态局部变量,1. 动态存储方式与静态动态存储方式 变量从变量的作用域(即空间)角度来分,可以分为全局变量和局部变量。从变量值存在的时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。 静态存储方式:是指在程序运行期间分配固定存储空间的方式。 动态存储方式:是在程序运行期间根据需要进行动态分配存储空间的方式。,用户存储空间一般可以分为三个部分: (1)程序区。 (2)静态存储区。 全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据
11、固定的存储单元,而不动态地进行分配和释放; (3)动态存储区。 动态存储区存放以下数据: 函数形式参数; 自动变量(未加static声明的局部变量); 函数调用时的现场保护和返回地址;,2. auto变量,函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。,3. 用static声明静态局部变量,如果想让函数中的局部变量的值在
12、函数调用结束后不消失而保留原值,那么就应该指定局部变量为“静态局部变量”,用关键字static进行声明。 对静态局部变量的定义需要注意: (1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。 (2)静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新赋一次初值,相当于执行一次赋值语句。 (3)如果在定义局部变量时不赋初值的话,对静态局部变量来说,编译时自动赋初值0或空字符;而对自动变量来说,如果不赋初值则它的值是一个不确定的
13、值,6.4.3寄存器变量和外部变量,1register变量 为了提高效率,C语言允许将局部变量的值放在CPU中的寄存器中,将这种变量称为寄存器变量,用关键字register作声明。,2. 用extern声明外部变量 外部变量(即全局变量)是在函数的外部定义的,它的作用域从变量定义开始,到本程序文件的末尾为止。如果外部变量不在文件的开头定义,其有效的作用范围只限于从定义到文件终位置。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。,6.5 编译预处理
14、,所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。,6.5.1宏定义,在语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。 宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。在语言中,“宏”分为有参数和无参数两种。,1. 无参宏定义 无参宏的宏
15、名后不带参数。其定义的一般形式为: #define 标识符 字符串,2. 带参数的宏定义 语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用时不仅要宏展开,而且要用实参去代换形参。 带参宏定义的一般形式为: #define 宏名(形参表) 字符串,6.5.2条件编译,预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。 条件编译有三种形式,1、第一种形式: #ifdef 宏名 程序段1 #else 程序段2 #endif,2、第二种形式: #ifndef 标识符 程序段1 #else 程序段2 #endif,3、第三种形式: #if 常量表达式 程序段1 #else 程序段2 #endif,