资源描述
C程序设计 讲师:徐红波 Email: x_h_ 第 8章 函数 8.1 概述 一个较大的程序一般应分为若干个 程序模块,每一个模块用来实现一个特 定的功能。所有的高级语句中都有子程 序这个概念,用子程序实现模块的功能。 在 C语言中,子程序的作用是由函数来完 成的。一个 C程序可由一个主函数和若干 个其他函数构成。由主函数调用其他函 数,其他函数也可以相互调用。同一个 函数可以被一个或多个函数调用任意多 次。 在程序设计中,常将一些常用的功 能模块编写成函数,放在公共函数库中 供大家选用。程序设计人员要善于利用 函数,以减少重复编写程序段的工作量。 例 8.1函数调用的简单例子 #include void main() void printstar(); void print_message(); printstar(); print_message(); printstar(); void printstar() printf(“*n”); void print_message() printf(“How do you do!n”); 说明: (1)一个 C程序由一个或多个程序模块组成,每一个程序模块作 为一个源程序文件。对较大的程序,一般不希望把所有内容全 放在一个文件中,而是将它们放在若干个源文件中,再由若干 个源程序文件组成一个 C程序。一个源程序文件可以为多个 C程 序共用。 (2)一个源程序文件由一个或多个函数以及其他有关内容 (如命令 行、数据定义等 )组成。一个源程序是一个编译单位,在程序编 译时是以源程序文件为单位进行编译的,而不是以函数为单位 进行编译的。 (3)C程序的执行是从 main函数开始的,如是在 main函数中调用 其他函数,在调用后流程返回到 main函数,在 main函数中结束 整个程序的运行。 (4)所有函数都是平行的,即在定义函数时是分别进行的,是相 互独立的。一个函数并不从属于另一个函数,即函数不能嵌套 定义。函数间可以相互调用,但不能调用 main函数。 main函数 是系统调用的。 (5)从用户使用的角度看,函数有两种。 标准函数 。标准函数即库函数,它是由系统提供的,用户不 必自己定义而直接使用它们。 用户自己定义的函数 。它是用以解决用户专门需要的函数。 (6)从函数的形式看,函数分两类。 无参函数 。在调用无参函数时,主调函数不向被调用函数传 递数据。无参函数一般用来执行指定的一组操作。无参函数 可以带回或不带会函数值,但一般以不带回函数值的居多。 有参函数 。在调用函数时,主调函数在调用被调用函数时, 通过参数向被调用函数传递数据,一般情况下,执行被调用 函数时会得到一个函数值,供主调函数使用。 8.2 函数定义的一般形式 8.2.1 无参函数定义的一般形式 定义无参函数的一般形式为: 类型标识符 函数名 () 声明部分 语句部分 在定义函数时要用“类型标识符”指定函数值的类型,即函 数带回来的值的类型。 8.2.2 有参函数定义的一般形式 定义有参函数的一般形式为: 类型标识符 函数名 (形式参数表列 ) 声明部分 语句部分 例如: int max(int x, int y) int z; z = xy ? x : y; return z; 8.2.3 空函数 在程序设计中有时会用到空函数,它的形式为: 类型说明符 函数名 () 8.3 函数参数和函数的值 8.3.1 形式参数和实际参数 在调用函数时,大多数情况下,主调函数和被调用 函数之间有数据传递关系。在定义函数时函数名后面括 号中的变量名称为“形式参数” (简称“形参” ),在主 调函数中调用一个函数时,函数名后面括号中的参数 (可以是一个表达式 )称为“实际参数” (简称“实参” )。 例 8.2调用函数时的数据传递 #include void main() int max(int x, int y); int a, b, c; scanf(“%d,%d”, c=max(a, b); printf(“Max is %d”, c); int max(int x, int y) int z; z = x y ? x : y; return z; 关于形参与实参的说明: (1)在定义函数中指定的形参,在未出现函数调用时,它们并不 占内存中的存储单元。只有在发生函数调用时,函数 max中的 形参才被分配内存单元。在调用结束后,形参所占的内存单元 也被释放。 (2)实参可以是常量、变量或表达式。但要求它们有确定的值。 在调用时将实参的值赋给形参。 (3)在被定义的函数中,必须指定形参的类型。 (4)实参与形参的类型应相同或赋值兼容。 (5)实参向形参的数据传递是“值传递”,单向传递,只由实参 传给形参,而不能由形参传回来给实参。在内存中,实参单元 与形参单元是不同的单元。在调用函数时,给形参分配存储单 元,并将实参对应的值传递给形参,调用结束后,形参单元被 释放,实参单元仍保留并维持原值。因此,在执行一个被调用 函数时,形参的值如果发生改变,并不会改变主调函数的实参 的值。 8.3.2 函数的返回值 通过函数调用使主调函数能得到一个确定的值,这就是函 数的返回值。 (1)函数的返回值是通过函数中的 return语句获得的。 return语句 将被调用函数中的一个确定值带回主调函数中去。 (2)函数值的类型。既然函数有返回值,这个值当然应属于某一 个确定的类型,应当在定义函数时指定函数值的类型。 (3)在定义函数时指定的函数类型一般应该和 return语句中的表 达式类型一致。如果函数值的类型和 return语句中表达式的值不 一致,则以函数类型为准。对数值型数据,可以自动进行类型 转换。即函数类型决定返回值的类型。 例 8.3返回值类型与函数类型不同 #include void main() int max(flaot x, float y); float a, b; int c; scanf(“%f,%f”, c=max(a, b); printf(“Max is %dn”, c); int max(float x, float y) float z; z = x y ? x : y; return z; (4)对于不带回值的函数,应当用“ void”定义函数为 “无类型” (或称“空类型” )。这样,系统就保证不 使函数带回任何值,即禁止调用函数中使用被调用函 数的返回值。此时在函数体中不得出现 return语句。 8.4 函数的调用 8.4.1 函数调用的一般形式 函数调用的一般形式为 函数名 (实参表列 ); 如果是调用无参函数,则“实参表列”可以没有, 但括号不能省略。如果实参表列包含多个实参,则各参 数间用逗号隔开。实参与形参的个数应相等,类型应匹 配。实参与形参按顺序对应,一一传递数据。但应说明, 如果实参表列包括多个实参,对实参求值的顺序并不是 确定的,有的系统按自左至右顺序求实参的值,有的系 统则按自右至左顺序。 例 8.4实参求值的顺序 #include void main() int f(int a, int b); int i=2, p; p=f(i, +i); printf(“%dn”, p); int f(int a, int b) int c; if(ab) c=1; else if(a=b) c=0; else c=-1; return c; 8.4.2 函数调用的方式 按函数在程序中出现的位置来分,可以有以下 3种函数调用 方式。 1. 函数语句 把函数调用作为一个语句。这时不要求函数带回值,只要 求函数完成一定的操作。 2. 函数表达式 函数出现在一个表达式中,这种表达式称为函数表达式。 这时要求函数带回一个确定的值以参加表达式的运算。 3. 函数参数 函数调用作为一个函数的实参。 8.4.3 对被调用函数的声明和函数原型 在一个函数中调用另一个函数需要具备的条件如下 。 (1)首先被调用的函数必须是已经存在的函数 (是库函数或用户自 己定义的函数 )。但光有这一条件还不够。 (2)如果使用库函数,还应该在本文件开头用 #include命令将调 用有关库函数时所需用到的信息“包含”到本文件中去。 (3)如果使用用户自己定义的函数,而该函数的位置在调用它的 函数 (即主调函数 )的后面 (在同一个文件中 ),应该在主调函数中 对被调用的函数作声明。 例 8.5对被调用的函数作声明 #include void main() float add(float x, float y); float a, b, c; scanf(“%f,%f”, c=add(a, b); printf(“sum is %fn”, c); float add(float x, float y) float z; z=x+y; return z; 在函数调用之前用函数原型做了函数声明。 因此编译系统记下了所需调用的函数的有关信息。 编译系统根据函数的原型对函数的调用的合法性 进行全面的检查。与函数原型不匹配的函数调用 会导致编译出错,它属于语法错误。用户根据屏 幕显示的出错信息很容易发现和纠正错误。 函数原型的一般形式有两种,分别为 (1)函数类型 函数名 (参数类型 1, 参数类型 2, , 参数类型 n); (2)函数类型 函数名 (参数类型 1 参数名 1, 参数类型 2 参数名 2, , 参数类型 n 参数名 n); 说明: (1)如果被调用函数的定义出现在主调函数之前,可以 不必加以声明。 (2)如果已在文件的开头 (在所有函数之前 ),已对本文 件中所调用的函数进行了声明,则在各函数中不必对 其所调用的函数再作声明。 8.5 函数的嵌套调用 C语言的函数定义是相互平行、独立的。在定义函 数时,一个函数内不能包含另一个函数。 C语言不能嵌套定义函数,但可以嵌套调用函数, 在调用一个函数的过程中,又调用另一个函数。 例 8.6函数嵌套调用的应用 m a i n 函 数 调 用 a 函 数 结 束 a 函 数 调 用 b 函 数 b 函 数 8.6 函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用 该函数本身,称为函数的递归调用。 f 函 数 调 用 f 函 数 f 1 函 数 调 用 f 2 函 数 f 2 函 数 调 用 f 1 函 数 ( a ) 直 接 递 归 调 用 ( b ) 间 接 递 归 调 用 例 8.7有 5个人坐在一起,问第 5个人多少岁?他说比第 4个人大 2岁。问第 4个人岁数,他说比第 3个人大 2岁。 问第 3个人,又说比第 2个人大 2岁。问第 2个人,说比第 1个人大 2岁。最后问第 1个人,他说是 10岁。请问第 5个 人多大 12)1( 110 )( nnage n nage 例 8.8用递归方法求 n! 1)!1( 1,01 ! nnn n n 8.9Hanoi塔问题。古代有一个梵塔,塔内有 3个座 A、 B、 C,开始时 A座上有 64个盘子,盘子大小不等,大的在 下,小的在上。有一个老和尚想把这 64个盘子从 A座移 到 C座,但每次只允许移动一个盘,且在移动过程中在 3 个座上都始终保持大盘在下,小盘在上。在移动过程中 可以利用 B座,要求编程序输出移动的步骤 A B C 8.7 数组作为函数参数 前面已经介绍了可以用变量作函数参数,显然,数 组元素也可以作函数参数,其用法与变量相同。此外, 数组名也可以作实参和形参,传递的是数组首元素的地 址。 8.7.1 数组元素作函数实参 由于实参可以是表达式,而数组元素可以是表达式的组成部 分,因此数组元素当然可以作为函数的实参,与用变量作实参一 样,是单向传递,即“值传送”方式。 例 8.10有两个数组 a和 b,各有 10个元素,将它们对应地逐个 比较 (即 a0与 b0比, a1与 b1比 ) 。如果 a数组中的元素大 于 b数组中的相应元素的数目多于 b数组中元素大于 a数组中相应 元素的数目 (例如, aibi6次, biai3次,其中 i每次为不同 的值 ),则认为 a数组大于 b数组,并分别统计出两个数组相应元 素大于、等于、小于的次数。 8.7.2 数组名作函数参数 可以用数组名作函数参数,此时形参应当用数组名 或用指针变量。 例 8.11有一个一维数组 score,内放 10个学生成绩, 求平均成绩 说明: (1)用数组名作函数参数,应该在主调函数和被调函数分别定义数 组。 (2)实参数组与形参数组类型应一致,如不一致,结果将出错。 (3)在被调用函数中声明了形参数组的大小为 10,但在实际上,指 定其大小是不起任何作用的,因为 C语言编译对形参数组大小不 做检查,只是将实参数组的首元素的地址传给形参数组。因此, 形参数组名获得了实参数组的首元素的地址。 (4)形参数组可以不指定大小,在定义数组时在数组名后面跟一个 空的方括号。有时为了在被调用函数中处理数组元素的需要,可 以另设一个形参,传递需要处理的数组元素的个数。 例 8.12形参数组不定义长度 (5)用数组名作函数实参时,不是把数组元素的值传递 给形参,而是把实参数组的首元素的地址传递给形参 数组,这样两个数组就共占同一段内存单元。形参数 组中各元素的值如发生变化会使实参数组元素的值同 时发生变化。 例 8.13用选择法对数组中 10个整数按由小到大排序。所 谓选择法就是先将 10个数中最小的数与 a0对换;再将 a1到 a9中最小的数与 a1对换 每比较一轮,找出 一个未经排序的数中最小的一个。共比较 9轮 8.7.3 多维数组名作函数参数 多维数组元素可以作函数参数。 用多维数组名作为函数的实参和形参,在被调用函 数中对形参数组定义时可以指定每一维的大小,也可以 省略第一维的大小说明。但是不能把第二维以及其他高 维的大小说明省略。 例 8.14有一个 3 4矩阵,求所有元素最大值 先使变量 max的初值为矩阵中第一个元素的值,然 后将矩阵中各个元素的值与 max相比,每次比较后都把 “大者”存放在 max中,全部元素比较完后, max的值 就是所有元素的最大值。 8.8 局部变量和全局变量 8.8.1 局部变量 在一个函数内部定义的变量是内部变量,它只在本函数范围内有效, 也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量 的。这称为“局部变量”。例如: float f1(int a) int b, c; char f2(int x, int y) int i, j; void main() int m, n; 说明: (1)主函数中定义的变量也只在主函数中有效,而不因 为在主函数中定义而在整个文件或程序中有效。主函 数也不能使用其他函数中定义的变量。 (2)不同函数中可以使用相同名字的变量,它们代表不 同的对象,互不干扰。 (3)形式参数也是局部变量。 (4)在一个函数内部,可以在复合语句中定义变量,这 些变量只在本复合语句中有效,这种复合语句也称为 “分程序”或“程序块”。 8.8.2 全局变量 程序的编译单位是源程序文件,一个源文件可以包 含一个或若干个函数。在函数内定义的变量是局部变量, 而在函数之外定义的变量称为外部变量,外部变量是全 局变量。全局变量可以为本文件中其他函数所共用。它 的有效范围为从定义变量的位置开始到本源文件结束。 例如: int p=1, q=5; float f1(int a) int b, c; char c1, c2; char f2(int x, int y) int i, j; void main() int m, n; 说明: (1)设置全局变量的作用是增加了函数间数据联系的渠道。 例 8.15有一个一维数组,内放 10个学生成绩,写一个函数,求 出平均分、最高分和最低分 (2)建议不在必要时不要使用全局变量,原因如下: 全局变量在程序的全部执行过程中都占用存储单元,而不是仅 在需要时才开辟单元。 它使函数的通用性降低了,因为函数在执行时要依赖于其所在 的外部变量。 使用全局变量过多,会降低程序的清晰性,难以清除地判断出 每个瞬时各个外部变量的值。 (3)如果在同一个源文件中,外部变量与局部变量同名,则在局部 变量的作用范围内,外部变量被“屏蔽”,即它不起作用。 例 8.16外部变量与局部变量同名 8.9 变量的存储类别 8.9.1 动态存储方式与静态存储方式 从变量的 作用域 (即从空间 )角度来分,可以分为 全 局变量 和 局部变量 。 从变量值存在的时间 (即 生存期 )角度来分,可以分 为 静态存储方式 和 动态存储方式 。 所谓静态存储方式是指在程序运行期间由系统分配 固定的存储空间的方式。而动态存储方式则是在程序运 行期间根据需要进行动态的分配存储空间的方式。 程序的存储空间可以分为三部分: 程序区 静态存储区 动态存储区 数据分别存放在静态存储区和动态存储区中。 全局变量全部存放在静态存储区中,在程序开始 执行时给全局变量分配存储区,程序执行完毕就 释放。在程序执行过程中它们占据固定的存储单 元,而不是动态地进行分配和释放。 在动态存储区中存放以下数据: 函数形式参数。在调用函数时给形参分配存储空间。 自动变量 (未加 static声明的局部变量 ) 函数调用时的现场保护和返回地址等。 对以上这些数据,在函数调用开始时分配动态存储空间,函 数结束时释放这些空间。在程序执行过程中,这种分配和释放是 动态的,如果在一个程序中两次调用同一函数,分配给此函数中 局部变量的存储空间地址可能是不同的。如果一个程序包含若干 个函数,每个函数中的局部变量的生存期并不等于整个程序的执 行周期,它只是程序执行周期的一部分。根据函数调用的需要, 动态地分配和释放存储空间。 在 C语言中,每一个变量和函数有两个属性:数据 类型和数据的存储类别。存储类别指的是数据在内存 中存储的方式。存储方式分为两大类:静态存储类和 动态存储类。具体包含 4种: 自动的 (auto)、静态的 (static)、寄存器的 (register)、外部的 (extern)。根据变量 的存储类别,可以知道变量的作用域和生存期。 8.9.2 auto变量 函数中的局部变量,如果不专门声明为 static存储类 别,都是动态地分配存储空间的,数据存储在动态存 储区中。函数中的形参和在函数中定义的变量 (包括在 复合语句中定义的变量 ),都属此类,在调用该函数时 系统会给它们分配存储空间,在函数调用结束时就自 动释放这些存储空间。因此这类局部变量称为自动变 量。自动变量用关键字 auto作存储类别的声明。 例如: int f(int a) auto int b, c=3; 8.9.3 用 static声明局部变量 有时希望函数中的局部变量的值在函数调用结束后 不消失而保留原值,即其占用的存储单元不释放,在下 一次该函数调用时,该变量已有值,就是上一次函数调 用结束时的值。这时就应该指定该局部变量为“静态局 部变量”,用关键字 static进行声明。 例 8.17考察静态局部变量的值 #include void main() int f(int); int a=2, i; for(i=0; i3; i+) printf(“%d “, f(a); int f(int a) auto int b=0; static int c=3; b=b+1; c=c+1; return (a+b+c); 对静态局部变量的说明 (1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。 在程序整个运行期间都不释放。而自动变量 (即动态局部变量 )属于动 态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结 束后即释放。 (2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序 运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留 上次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行 的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当 于执行一次赋值语句。 (3)如在定义局部变量时不赋初值的话,则对静态局部变量来说,编 译时自动赋初值 0(对数值型变量 )或空字符 (对字符变量 )。而对自动变 量而说,如果不赋初值则它的值是一个不确定的值。这是由于每次函 数调用结束后存储单元已释放,下次调用时又重新另分配存储单元, 而所分配的单元中的值是不确定的。 (4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不 能引用它的。 需要用静态局部变量的情况如下。 (1)需要保留函数上一次调用结束时的值。 例 8.18输出 1到 5的阶乘值 (2)如果初始化后,变量只被引用而不改变其值,则这时用静态局 部变量比较方便,以免每次调用时重新赋值。 但是应该看到,用静态存储要多占内存 (长期占用不释放,而 不能像动态存储那样一个存储单元可供多个变量使用,节约内 存 ),而且降低了程序的可读性,当调用次数多时往往弄不清静 态局部变量的当前值是什么。因此,若非必要,不要多用静态局 部变量。 8.9.4 register变量 一般情况下,变量 (包括静态存储方式和动态存储方式 )的 值是存放在内存中的。当程序中用到哪一个变量的值时,由控 制器发出指令将内存中该变量的值送到运算器中。经过运算器 进行运算,如果需要存数,再从运算器将数据送到内存存放。 如果有一些变量使用频繁 (例如,在一个函数中执行 10000 次循环,每次循环中都要引用某局部变量 ),则为存取变量的值 要花费不少时间。为提高执行效率, C语言允许将局部变量的 值放在 CPU中的寄存器中,需要用时直接从寄存器取出参加运 算,不必再到内存中去存取。由于对寄存器的存取速度远高于 对内存的存取速度,因此这样做可以提高执行效率。这种变量 叫做寄存器变量,用关键字 register作声明。 例 8.19使用寄存器变量 #include void main() long fac(long); long i, n; scanf(“%ld”, for(i=1; i=n; i+) printf(“%ld!=%ldn”, i, fac(i); long fac(long n) register long i, f=1; for(i=1; i=n; i+) f=f*i; return f; 说明: (1)只有局部自动变量和形式参数可以作为寄存器变量,其他 (如 全局变量 )不行,在调用一个函数时占用一些寄存器以存放寄存 器变量的值,函数调用结束释放寄存器。此后,在调用另一个 函数时又可以利用它来存放该函数的寄存器变量。 (2)一个计算机系统中的寄存器数目是有限的,不能定义任意多 个寄存器变量。不同的系统允许使用的寄存器数目是有限的, 而且对 register变量的处理方法也是不同的,有的系统对 register 变量当作自动变量处理,分配内存单元,并不真正把它们存放 在寄存器中,有的系统只允许将 int、 char和指针型变量定义为 寄存器变量。 (3)局部静态变量不能定义为寄存器变量。 8.9.5 用 extern声明外部变量 外部变量是在函数的外部定义的全局变量,它的作 用域是从变量的定义处开始,到本程序文件的末尾。 在此作用域内,全局变量可以为程序中各个函数所引 用。编译时将外部变量分配在静态存储区。 有时需要用 extern来声明外部变量,以扩展外部变 量的作用域。 1、在一个文件内声明外部变量 如果外部变量不在文件的开头定义,其有效的作用 范围只限于定义处到文件结束。如果在定义点之前的函 数想引用该外部变量,则应该在引用之前用关键字 extern对该变量作“外部变量声明”,表示该变量是一 个已经定义的外部变量。有了此声明,就可以从“声明” 处起,合法地使用该外部变量。 例 8.20用 extern声明外部变量,扩展它在程序文 件中的作用域 #include void main() int max(int, int); extern A, B; printf(“%dn”, max(A, B); int A=13, B=-8; int max(int x, int y) int z; z=xy ? x : y; return z; 2、在多文件的程序中声明外部变量 如果一个程序包含两个文件,在两个文件中都要用 到同一个外部变量 Num,不能分别在两个文件中各自 定义一个外部变量 Num,否则在进行程序的连接时会 出现“重复定义”的错误。正确的做法是:在任一个 文件中定义外部变量 Num,而在另一个文件中用 extern 对 Num作“外部变量声明”。即“ extern Num;”。在编 译和连接时,系统会由此知道 Num是一个已在别处定 义的外部变量,并将在另一文件中定义的外部变量的 作用域扩展到本文件,在本文件中可以合法地引用外 部变量 Num。 例 8.21用 extern将外部变量的作用域扩展到 其他文件 #include int A; void main() int power(int); int b=3, c, d, m; printf(“enter the number a and its power m:n”); scanf(“%d, %d”, c=A*b; printf(“%d*%d=%dn”, A, m, d); extern A; int power(int n) int i, y=1; for(i=1; i=n; i+) y*=A; return y; 8.9.6 用 static声明外部变量 有时在程序设计中希望某些外部变量只限于被本文件引 用,而不能被其他文件引用。这时可以在定义外部变量时加 一个 static声明。 例如: file1.c file2.c static int A; extern int A; void main() void fun(int n) A=A*n; 8.9.7 关于变量的声明和定义 对变量而言,声明与定义的关系稍微复杂一些。在声明部 分出现的变量有两种情况:一种是需要建立存储空间的 (如: int a;),另一种是不需要建立存储空间 (如: extern a;)。前者称为 “定义性声明”,或简称定义。后者称为“引用性声明”。广 义地说,声明包括定义,但并非所有的声明都是定义。对“ int a;”而言,它既是声明,又是定义。而对“ extern a;”而言,它是 声明而不是定义。一般为了叙述方便,把建立存储空间的声明 称定义,而把不需要建立存储空间的声明称为声明。显然这里 指的声明是狭义的,即非定义性声明。例如: void main() extern A; int A; 8.9.8 存储类别小结 从上可知,对一个数据的定义,需要指定两种属 性:数据类型和存储类别,分别使用两个关键字。 例如: static int a; auto char c; register int d; 此外,可以用 extern声明变量为已定义的外部变 量,例如: extern b; 下面从不同角度做些归纳: (1)从作用域角度分,有局部变量和全局变量。它们采用 的存储类别如下: 局部变量 : 自动变量,即动态局部变量 (离开函数,值就消失 ) 静态局部变量 (离开函数,值仍保留 ) 寄存器变量 (离开函数,值就消失 ) (形式参数可以定义为自动变量和寄存器变量 ) 全局变量 : 静态外部变量 (只限本文件引用 ) 外部变量 (即非静态的外部变量,允许其他文件引用 ) (2)从变量存在的时间 (生存期 )来区分,有动态存储和 静态存储两种类型。静态存储是程序整个运行时间都 存在,而动态存储则是在调用函数时临时分配单元。 动态存储 : 自动变量 (本函数内有效 ) 寄存器变量 (本函数内有效 ) 形式参数 (本函数内有效 ) 静态存储 : 静态局部变量 (函数内有效 ) 静态外部变量 (本文件内有效 ) 外部变量 (其他文件可引用 ) (3)从变量值存放的位置来区分,可分为: 内存中静态存储区 : 静态局部变量 静态外部变量 (函数外部静态变量 ) 外部变量 (可为其他文件引用 ) 内存中动态存储区 : 自动变量和形式参数 CPU中的寄存器 : 寄存器变量 (4)对一个变量的性质可以从两个方面分析,一是变量的 作用域,一是变量值存在时间的长短,即生存期。前者 是从空间的角度,后者是从时间的角度。二者有联系但 不是同一回事。 (5)static对局部变量和全局变量的作用不同。对局部变 量来说,它使变量由动态存储方式改变为静态存储方式。 而对全局变量来说,它使变量局部化 (局部于本文件 ), 但仍为静态存储方式。从作用域角度看,凡有 static声明 的,其作用域都是局限的,或者是局限于本函数内 (静 态局部变量 ),或者局限于本文件内 (静态外部变量 )。 8.10 内部函数和外部函数 函数本质上是全局的,因为一个函数要被另 外的函数调用,但是,也可以指定函数不能被其 他文件调用。根据函数能否被其他源文件调用, 将函数区分为 内部函数 和 外部函数 。 8.10.1 内部函数 如果一个函数只能被本文件中其他函数所调用,它 称为内部函数。在定义内部函数时,在函数名和函数类 型的前面加 static,即: static 类型标识符 函数名 (形参表 ); 内部函数又称静态函数,因为它是用 static声明的。 使用内部函数,可以使函数的作用域只局限于所在文件, 在不同的文件中有相同的内部函数,互补干扰。这样不 同的人可以分别编写不同的函数,而不必担心所用函数 是否会与其他文件中函数同名。 8.10.2 外部函数 (1)在定义函数时,如果在函数首部的最左端加关键字 extern,则表示此函数是外部函数,可供其他文件调用。 (2)在需要引用此函数的文件中,用 extern对函数作声 明,表示该函数是在其他文件中定义的外部函数。 例 8.22有一个字符串,内有若干个字符,今输入一个 字符,要求程序将字符串中该字符删去。用外部函数 实现。 习题 8.3 写一个判素数的函数,在主函数输入一个整数,输 出是否素数的信息。 8.4 写一个函数,使给定的一个 3 3的二维整型数组转 置,即行列互换。 8.5 写一个函数,使输入的一个字符串按反序存放,在 主函数中输入和输出字符串。 8.6 写一个函数,将两个字符串连接。 8.13 用递归方法求 n阶勒让德多项式的值,递归公式为: 1/)(*)1()(*)12( 1 01 )( 21 nnxpnxpxn nx n xp nn n
展开阅读全文