第 7章 函数
7.1 函数概述
7.2 函数的定义
7.3 函数的声明和调用
7.4 参数的传递
7.5 嵌套调用和递归调用
7.6 指针与函数
7.7 数组作为函数参数
7.8 变量的作用域和存储类别
7.9 外部函数和内部函数
7.10 命令行参数
7.11 实例演示:“极限俱乐部会员信息系统”程序
7.1概述
为了使程序大而不繁,简洁明了,程序设计者要根据软件的总体要求,把相同功能或相似功能的操作归纳成模块的形式,并设计成函数,以实现程序设计的模块化。所以函数是 C程序的重要成分,它封装了完成某一特定功能的程序代码和数据,实现较高级的抽象。除函数的具体设计者外,其他人员只要运用已经设计好的函数,
知道函数原型,懂得如何调用,即知道函数的对外接口,
无须知道函数内部的具体实现细节,这有利于提高程序的开发效率,同时增强程序的可靠性。
7.1函数概述
C语言不仅提供了极为丰富的库函数 (如 Turbo C,MS
C都提供了三百多个库函数 ),还允许用户建立自己定义的函数。在功能上,由主函数调用其他函数,其他函数也可以互相调用,
C语言的函数作为一个模块应具备下面两个原则:
界面清晰。函数的处理子任务明确,函数之间数据传递越少越好。
大小适中。若函数太大,处理任务复杂,导致结构复杂,
程序可读性较差;反之,若函数太小,则程序调用关系复杂,这样会降低程序的效率 。
函数分类
在 C语言中,函数可分为两类,一类是由系统定义的标准函数,又称为库函数,其函数声明一般是在系统的include目录下以,h为后缀的头文件中,如在程序中要用到某个库函数,必须在调用该函数之前用 #include<头文件名 >命令将库函数信息包含到本程序中。
另一类函数是自定义函数,这类函数是根据问题的特殊要求而设计的,自定义的函数为构架复杂的大程序提供了方便,同时对程序的维护和扩充也带来了一些便利。对于自定义函数,在程序中可表现为以下两种形式。
第一种:函数声明、函数调用、函数定义。
第二种:函数定义、函数调用。
函数调用
一个 C程序由主函数和若干个或 0个用户函数组成。 C语言中的函数没有隶属关系,即所有的函数都是独立定义的,不能嵌套定义,函数是通过调用来执行的,允许函数间互相调用,也允许直接或间接的递归调用其自身。
main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。
调用另一个函数的函数称为主调函数,被调用的函数称为被调函数。一个函数调用另一个函数时是将流程控制转到被调函数,被调函数执行完后返回主调函数的调用处继续主调函数的执行。
7.2 函数的定义
调用函数必须遵循“定义在先、使用在后”的原则。函数定义的一般格式,
类型说明 函数名 (类型说明符 形参变量 1,类型说明符形参变量 2,…)
{
语句部分
}
1.函数头
包括函数名、函数的类型及形式参数表。
( 1)函数名
用标识符表示,用来标识一个函数的名字,函数名后面必须有一对圆括号。除 main函数外,其他函数可以按标识符规则任意命名,程序风格要求函数命名是能反映函数功能、有助于记忆的标识符。
( 2)函数类型
函数名前的函数类型是指函数返回值的类型。如为整型 int可省略不写;如无返回值,以 void类型明示。如果函数为无参函数,
而且在调用后没有返回值。在这种情况下,C语言规定应当将函数类型说明为 void型 (空值类型 ),否则,可能导致程序出错。
( 3)形式参数表
圆括号内为形式参数表列部分,其中的参数称为形式参数,它包括函数的自变量部分。每个参数前都应用相应的类型标识符对参数进行说明,有多个参数时用逗号分开。如果是无参数函数,形式参数表列部分为空,但一对 ()不能省略。
2.函数体
函数体是函数头下面最外层一对花括号内的代码,它是由一系列语句构成的,用以实现函数的功能,函数体内可以有函数说明、变量说明以及可执行语句。
函数体也可以是一对空的花括号,例如:
void dummy(void)
{
}
这是一个“空函数”,调用它并不产生任何有效的操作,
但却是一个符合 C语言语法的合法函数。在程序开发过程中,通常先开发主要函数,一些次要的函数或有待以后扩充和完善功能的函数暂时写成空函数,使程序可以在不完整情况下调试部分功能。当函数执行到 return语句或执行完函数体所有语句,流程就回到主调函数。
关于 return语句说明如下:
return语句是函数的逻辑结尾,不一定是函数的最后一条语句,一个函数中允许出现多个 return语句,但每次只能有一个 return语句被执行。
如果不需要从被调函数带回返回值可以不要 return语句。
一般情况是将函数类型定义为 void型,也叫空类型,此种类型的函数一般用来完成某种操作例如输出程序运行结果等。
还可以用不带表达式的 reture作为函数和逻辑结尾,这时,reture的作用是将控制权交给调用函数,而不是返回一个值。
7.3 函数的说明和调用
不同的函数实现各自的功能,完成各自的任务 。 要将它们组织起来,按一定顺序执行,是通过函数调用来实现的 。
主调函数通过函数调用向被凋函数进行数据传送,控制转移;
被凋函数在完成自己的任务后,又会将结果数据回传给主调函数并交回控制权 。
各函数之间就是这样在不同时间,情况下实行有序的调用,共同来完成程序规定的任务 。
如果一个源程序包含了多个函数,而函数间又有相互调用,那将会形成一种复杂的局面,使,定义在前,使用在后,的原则难以实现 。 为此,C语言通过函数声明语句解决这个问题 。
7.3.1 函数的声明
函数声明语句的般格式:
类型说明符 函数名 (类型说明符 形参变量 1,类型说明符 形参变量 2,…);
从形式上看,函数声明就是在函数定义格式的基础上去掉了函数体。通常,将函数声明安排在源文件的开始部分。函数声明中的形参变量名可以省略。
函数声明是语句,所以最后的结束符,;”不可缺少。使用函数声明后,可以将函数的定义放在源程序的后部。
函数说明通常出现在程序的开头,第一个函数定义之前,
也可放在主调函数的开头。有了函数说明,编译系统就对函数每次调用进行检查,将函数说明和函数调用进行对比,以保证调用时使用的参数、类型、返回值类型都是正确的。
7.3.2 函数的调用
一个函数写好后,若不通过函数调用,是不发挥任何作用的,
函数调用是通过函数调用语句来实现的 。 函数调用时会去执行函数语句中的内容,函数执行完毕后,回到函数的调用处,
继续执行下面的语句 。
函数调用有三种方式:
( 1)函数语句
把一个函数作为一个语句。如,dy();”。这些不要求函数返回值,只要求函数完成一定的操作。
( 2)函数表达式
函数出现在一个表达式中,这种表达式称为函数表达式,这时要求函返回一个确定的值以参加表达式的运算。如
,c=ftoc(f);”。
( 3)函数参数
函数的调用作为一个函数的参数,这时要求函数返回一个确定的值作为另一个函数的实际参数。如
,m=max(max(x,y),z);”。
在编写程序、进行函数调用时,注意几点:
C语言参数传递时,主调函数中实参向被调函数中形参的数据传送一般采用传值方式,把各个实参值分别顺序对应地传给形参。被调函数执行中形参值的变化不会影响主调函数中实参变量的值。但数组名作为参数传送时不同,它是“传址”,会对主调函数中的数组元素产生影响。
由于采用传值方式,实参表列中参数允许为表达式及常量。尤其值得注意的是,对实参表达式求值,C语言并没有规定求值的顺序。采用自右至左或自左至右顺序的系统均有。许多 C(Turbo C,MS C)是采用自右至左的顺序求值的。遇此情形,为保证函数调用能得到正确的执行结果,编写程序时应尽量采用其他可行的办去,加以避免为好。
在编写程序、进行函数调用时,注意几点:
注意采用函数原型声明对被调函数参数类型进行说明。
如不作说明,C语言无法进行实参类型的检查与转换。
稍有疏忽,会造成实参个数、类型与形参个数、类型不符,将引起参数传送出错,导致运算结果的大相径庭。
函数调用也是一种表达式,其值就是函数的返回值。
函数调用时,被调用函数可以是库函数或用户自定义的函数。如果是库函数,一定要在本文件的开头加上头文件。
7.4 参数的传递
7.4.1 普通值传递
值传递方式所传递的是参数值 。 调用函数时,将实参的值计算出来传递给对应的形参 。
说明:
( 1)实参可以是常量或表达式,也可以是函数调用语句,
如以下函数调用,num_of_primes(4,1000)、
num_of_primes(5,x*y),abs_sum(abs_sum(x,y),z)等,
都是正确的。
( 2)实参与形参不共用存储单元,即使同名,形参值的改变也不会影响主凋函数实参变量的值。
( 3)实参与形参的类型应该匹配,由实参将数据传送给形参。
( 4)对于有返回值的函数,调用时若没有把它赋给某个变量,仍然是可以的,只是函数的返回值有可能会被丢失。
7.4.2 地址值传递
地址值传递指的是调用时给出的实参是变量的地址值,
此时函数参数 (形参 )应该是指针变量。
形参指针得到某变量的地址值后,形参指针就直接指向该变量,所以在执行函数体的过程中形参指针通过间接访问可以改变这个变量的值,这是地址值传递的特点,
利用这一点,在需要时调用函数后可以“返回”多个值。
但是地址值的调用还是传值调用,此时传的是地址值,
其传值的单向性没有改变,即形参指针在函数执行过程中的改变不影响实参 (地址值 ).
7.5 嵌套调用和递归调用
7.5.1 嵌套调用
函数的嵌套调用指的是函数调用函数,实际上前面的程序中都有主函数调用常用的输入输出函数,或调用数学函数,或调用字符串函数,或调用自定义函数的例子,
只不过是主函数在调用它们而已,从广义来说也是嵌套调用。
函数的嵌套调用更多的是指函数的连环调用。
函数嵌套调用过程中,每次调用函数时系统都要开辟一个新的内存区域用来存放被调用函数的代码和记录当时调用函数的状态和返回地址等。所以嵌套调用层次越多,
时空开销越大 (即运行时间长,占用内存空间多 )。
7.5.2 递归调用
一个函数在它的函数体内调用它自身称为递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层。
C语言中允许在函数中调用函数自身,或函数之间相互调用,这种调用方式称之为递归。根据不同的调用方式,
递归又分为直接递归调用和间接递归调用。
直接递归调用:函数直接调用自身。
间接递归调用:函数互相调用对方。
在递归函数的调用中,问题的求解分为两个阶段:第一阶段是“回推”,即从所要求的问题回推到递归的结束条件处 ;第二阶段是“递推”,即再从递归的结束条件递推到要求的问题,这时才可以求出结果。
7.6 指针与函数
7.6.1指针变量作函数参数
#include<stdio.h>
void swap(int,int);
void main()
{ int a=5,b=9;
printf("%d,%d\n",a,b);
swap(a,b);
printf("%d,%d\n",a,b);}
void swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;}
运行结果:
7.6.2 返回值为指针值的函数
#include<stdio.h>
int larger1(int,int);
int *larger2(int*,int*);
void main()
{ int a,b,bigger1,*bigger2;
printf("input two interger numbers,");
scanf("%d,%d",&a,&b);
bigger1=larger1(a,b); /*函数 larger1返回指向变量的值 */
printf("The larger number is %d\n",bigger1);
bigger2=larger2(&a,&b); /*函数 1argcr2返回指向整型变量的指针值 */
printf("The larger number is %d\n",*bigger2);}
int larger1(int x,int y)
{ return x>y?x:y;}
int *larger2(int*x,int*y)
{ return *x>*y?x:y;}
运行结果为:
7.6.3 函数的指针和指向函数的指针变量
定义一个指针变量,让它指向某个函数,这个变量就称为指向函数的指针变量。利用指向函数的指针变量的作用是更灵活地进行函数调用。函数指针的定义格式如下:
数据类型 (*函数指针变量名 )();
说明:
(1)在定义函数指针时,使用圆括号括起函数指针变量名。
(2)其中的“数据类型”是指函数返回值的类型。
阅读下面的程序,了解函数指针的使用
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
void main()
{
int(*p)(int,int);
p=max;
printf("The max of 3 and 4 is %d\n",(*p)(3,4));
}
程序运行结果:
7.7 数组作为函数参数
数组用作函数参数有两种形式,一种是把数组元素 (下标变量 )作为实参使用;另一种是把数组名作为函数的形参和实参使用。
用数组名作函数参数时,应该注意如下几个方面
①数组名作为函数参数,应该在主调函数与被调函数中分别定义数组,不能只在一方定义。
②要注意用数组元素作实参和数组名作实参的区别:用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。
而用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。
用数组名作函数参数时,应该注意如下几个方面
③ 实参数组和形参数组大小不要求一致,因为传送时只是将实参数组的首地址传给形参数组。因此,一维形参数组也可以不指定大小,在定义数组时,在数组名后跟一个空的方括弧。
被调函数涉及对数组元素的处理,可另设一个参数来指明数组元素的个数。
④数组名作为函数参数,传送整个数组数据,不是用实参数组与形参数组元素间的“传值”方式,而是用把实参数组的首地址传给形参数组的“传址”方式。形参数组不会被分配内存空间,它与实参数组共用一段内存空间。在“传址”方式下,由于共用一段内存空间,形参数组中各元素值的变化直接影响到实参数组元素值的变化。
⑤ 多维数组名也可以作为实参和形参,使用时,也应该在主调函数与被调函数中分别定义数组,数组类型也应该一致,这样才不致出错。另外,在被调函数中对形参数组定义时,至多也只能省略第一维的大小说明。这是因为多维数组的数组元素是按连续地址存放的,不给出各维的长度说明,将无法判定数组元素的存储地址。也不能只指定第一维,不指定第二维以后的大小 (因实参、
形参数组可以大小不一致 )。
用数组名作函数参数时,应该注意如下几个方面
7.8变量的作用域和存储类别
7.8.1 局部变量和全局变量
1.局部变量
不管是主函数还是其他函数,在函数内说明的变量 (包括函数参数 )统称为局部变量。
在没有其他修饰符的情况下,局部变量的特点是随函数的调用而生成,随函数调用结束而释放。
形参变量只有在函数内才是有效的,离开该函数就不能再使用了。
程序中的大多数变量是局部变量。不同的函数在说明局部变量时可能会同名,这不会引起混淆或冲突,因为它们各自只在自己的函数内有效。
局部变量小结:
在主函数 main()中定义的局部变量,只能在主函数中使用,其他函数不能使用。同时,主函数中也不能使用其他函数中定义的局部变量。因为主函数也是一个函数,
与其他函数在定义上是平行关系,应予以注意。
形参变量是局部变量,属于被调用函数。实参变量是属于主调函数的局部变量。
允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会混淆。
在复合语句中也可定义变量,其作用域只在复合语句范围内。
2.全局变量
假如变量是定义在函数外面 (它们不属于任何一个函数 ),
那么,这些变量自定义开始就存在了,一直到程序运行结束。定义在函数外面的变量统称全局变量,自定义开始,它后面的任何函数都可以访问它 (条件是函数内没有同名的局部变量或定义变量时没有其他修饰符 )。全局变量的这个特点在程序中既带来方便,也带来“隐患”,因为它破坏了模块的封装性和数据的安全性,由此建议限量应用。
全局变量优点
增加了各函数间数据传送的渠道。特别是函数返回值通常仅限于一个,这在很多场合不能满足使用要求。此时利用全局变量,可以得到更多的数据处理结果。
利用全局变量可以减少函数实参与形参的个数。其带来的好处是减少函数调用时分配的内存空间以及数据传送所必需的传送时间。
全局变量的缺点
全局变量的作用范围大,为此必然要付出的代价是其占用存储单元时间长。它在程序的全部执行过程都占据着存储单元,不像局部变量那样仅在一个函数的执行过程中临时占用存储单元。
函数过多使用全局变量,降低了函数使用的通用性。通过外部变量传递数据也增加了函数间的相互影响,函数的独立性、封闭性、可移植性大大降低,出错的几率增大。
多人合作完成的程序通常由多个源文件组成。一旦出现全局变量同名的情况,将引起程序出错。
使用全局变量过多,会降低程序的清晰度,使人难以判断某一时刻各个全局变量的值,因为各个函数在执行时都可以改变全局变量的值。
7.8.2变量的存储类别
变量占用内存单元的时间称为 "生存期 ",变量的生存期是由变量的存储位置决定的。程序中使用的数据可存放在 CPU的寄存器和内存储器中。
CPU寄存器,CPU寄存器中存储的数据是动态存储类型,
不能长期占用。
内存:内存中又分为两个区域,即静态存储区和动态存储区。其中,静态存储区存放的数据可以在程序执行期间长期占有内存单元,直到该程序结束;动态存储区存放的数据是临时占用内存单元,当某一程序段执行完毕,
系统收回内存单元。
数据的存储类型及存储地点存储类型 存储类型符 存储地点自动型 auto 内存动态存储区寄存器型 register CPU寄存器静态型 static 静态存储区外部变量 Extern 静态存储区不同存储类别的变量:
( 1)自动变量 (auto)
存储在堆区和栈区的变量,随函数调用而生成,随函数调用结束而释放,C语言从存储的角度称这些变量为自动变量,
( 2)静态变量
静态变量存放在静态存储区中。静态变量在变量定义时就分配了固定的内存单元,并根据所定义的数据类型存入默认值,在程序运行中一直占用内存单元不释放,直到程序运行结束才释放。
静态变量
静态变量又分为静态局部变量和静态全局变量。
静态局部变量
在函数或复合语句中定义的静态变量,称为静态局部变量,该变量在其函数中或复合语句中有效。静态局部变量所在的函数无论调用多少次,静态局部变量赋值语句只执行一次,但能够保留住函数多次调用的中间结果,
这就相对保持了程序的独立性。
静态全局变量
在函数体外定义的变量称为全局变量。使用关键字
"static"定义的全局变量称为静态全局变量,该变量只在本文件中有效,其他的文件不可以使用此变量 (这一点要与外部变量加以区分 )。
( 3)外部变量 (extern)
C语言允许将一个源文件程序清单分放在若干个程序文件中 (若干个,c文件 ),采用分块编译方法编译生成一个目标程序 (,obj文件 )。其中每个程序称为一个 "编译单位 ",最后,将它们连接在一起生成,exe文件,从而达到提高编译速度和便于管理大型软件工程的目的。Tubro C系统规定在某一个源程序中定义的全局变量,
其他的多个文件可以使用,这就是所说的程序间的数据交流,在 C语言中称为外部变量。
外部变量使用关键字 "extern"定义,在函数体之外定义,
存放在静态存储区。
( 4)寄存器变量 (register)
有一定计算机基础的读者都知道计算机内部寄存器的存取速度比内存中存储单元的存取速度快得多。为了提高某些经常使用变量的存取速度,Turbo C语言提供了寄存器变量。这种变量使用的是 CPU内的寄存器,不需要通过访问内存来确定和修改其数值。寄存器变量与一般自动变量相比,它可以提高存取速度一个数量级或更多。
其定义格式为:
register类型名 变量名 ;
7.9外部函数和内部函数
7.9.1 内部函数
如果在一个源文件中定义的函数只能被本文件中的函数调用,而不能被其他文件中的函数调用,这种函数称为内部函数。使用保留字 static定义内部函数,其格式为:
static数据类型 函数名 (形参表 );
7.9.2 外部函数
前面定义的函数可以说都是外部函数,它们可以被本文件内调用,也可以被外部文件 (当程序是由多文件组成时 )调用。但是在外部文件调用时,外部文件在调用前需做如下说明:
extern数据类型 函数名 (形参表 );
7.10命令行参数
在 C语言中,main函数也可以带参数,参数的个数最多为 3个,但参数名及参数的顺序和类型是固定的,形式如下:
main(int argc,char *argv[],char *env[])
根据 C语言的规则,main函数的形式可有如下几种:
① main()
② main(int argc)
③ main(int argc,char*argv[])
④ main(int argc,char*argv[],char*env[])
其中第②种情况不常用。
C源程序的特点:
程序一般用小写字母书写;
大多数语句结尾必须要用 ";"作为终止符,否则 C不认为该语句结束;
每个程序必须有一个而且只能有一个称作主函数的
main()函数;
每个程序体(主函数和每个子函数,如上例中的 main()
函数和 sub()函数)必须用一对花括号 "{"和 "}"括起来;
一个较完整的程序大致包括,包含文件 (一组
#include<*.h>语句 )、用户函数说明部分、全程变量定义、主函数和若干子函数组成。在主函数和子函数中又包括局部变量定义、若干个 C库函数、控制流程语句、
用户函数的调用语句等;
注释部分包含在 "/*"和 "*/"之间,在编译时它被 C编译器忽略。
说明:
象其它一些语言一样,C的变量在使用之前必须先定义其数据类型,未经定义的变量不能使用。定义变量类型应在可执行语句前面。
C中,大、小写字母是有区别的,相同字母的大、小写代表不同的变量。
C程序的书写格式非常灵活,没有严格限制。
7.11实例演示:“极限俱乐部会员信息系统”程序
/*系统密码,123321*/
/*极限俱乐部会员信息系统 */
#include "stdio.h" /*控制台 I/O的头文件 */
#include "string.h" /*字符串函数的头文件 */
#include "stdlib.h" /*exit()函数的头文件 */
#define M 20 /*定义最多可录入的单位数 */
#define N 100 /*定义最多可录入的会员数 */
int Interface(char (*x)[20],int n); /*操作界面函数 */
int AddList(int start,char Unit[][20],char Name[][20],
char Number[][20],int Parameter[][5]); /*添加信息函数 */
void Display(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int start,int end); /*显示信息函数 */
程序接上页
void SwapChar(char *x,char *y); /*字符互换函数 */
void SwapeInt(int *x,int *y); /*整型数互换函数 */
void Sort(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*按姓名排序函数 */
int Search (char (*x)[20],char (*y)[20],char (*z)[20],
char *person,int n); /*信息检索函数 */
int FindStaff(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*信息查找函数 */
void Modify(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*信息修改函数 */
int Delete(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*信息删除函数 */
char password[8]="7654321"; /*/设置系统管理员密码 *
程序接上页
/**********************************************************\
主函数
\**********************************************************/
void main()
{
char Unit[M][20]; /*单位 */
char Number[N][20]; /*编号 */
char Name[N][20]; /*姓名 */
int Parameter[N][5]; /*成绩 */
int select;
int n=0;
char mainName[][20]={"添加信息 ","显示信息 ","按姓名排序 ",
"按姓名检索 ","修改信息 ","删除信息 "}; /*系统菜单
*/
程序接上页
/**********************系统密码管理 ********************/
char choice;
char s[8];
int flag = 0;
int w = 3; /*设置输入密码最多允许连续出错的次数
*/
do
{
printf("请输入管理员密码,\n");
scanf("%s",s);
if(!strcmp(s,password)) /*验证密码 */
{
printf("PASS\n\n\n"); /*密码正确,则输出,pass”*/
flag = 1;
break;
}
程序接上页
else
{
printf("密码错误,请重新输入,\n");
w--;
}
}
while(w>0);
if(!flag)
{
printf("你已连续三次输入错误,请确认后再使用本系统,谢谢 !\n");
exit(0);
}
system("cls"); /*用 system调用 dos命令,清屏 */
程序接上页
/*************************欢迎界面 ************************************/
printf("~~~~~~~~~~~~~\t\t\t\t\t~~~~~~~~~~~~~\n");
printf("\t\t欢迎进入极限俱乐部会员信息系统 \n"); /*标题 */
printf("\n%%%%%%%%%%%%%%%%%%%%%%%%%\t\t\t\t\t%%%%
%%%%%%%%%%%%%%%%%%%%%%%\n");
printf("\n\n\n\n");
printf("单位,北京极限俱乐部 \n"); /*用户单位 */
printf("地址北京市西城区西四 \n");
printf("会员热线,010-66886688");
printf("\n\n\n\n");
printf("==============\t\t==============\n");
printf("本系统由“天地人和软件有限公司”开发 \n"); /*开发商 */
printf("作者:天天 \n");
printf("地址:北京市海淀区学院路 \n");
printf("==============\t\t==============\n");
printf("\n\n");
printf("\t\t请任意输入一个值并回车进入系统 \n");
scanf("%s",&choice);
if(choice== '\n'||choice== 'N')
exit(1);
程序接上页
/***********************欢迎界面结束 ******************************/
system("cls"); /*通过 system()函数调用 dos命令,清屏 */
do
{
select=Interface(mainName,6);
switch (select) /*转向语句,选择以实现不同的功能 */
{
case 0,n=AddList(n,Unit,Number,Name,Parameter);break;
case 1,Display(Unit,Number,Name,Parameter,0,n-1);break;
case 2,Sort(Unit,Number,Name,Parameter,n);break;
case 3,FindStaff(Unit,Number,Name,Parameter,n);break;
case 4,Modify(Unit,Number,Name,Parameter,n);break;
case 5,n=Delete(Unit,Number,Name,Parameter,n);break;
}
}
while(select>=0&&select<=5);
}
程序接上页
/**********************************************************\
操作界面函数
\**********************************************************/
int Interface(char (*x)[20],int n)
{
int i=0;
int j;
printf("\n××××××× 极限俱乐部会员信息系统 ××××××× \n");
do
{
for (j=0;j<n;j++)
printf("%2d.%s\n",j+1,x[j]);
printf("%2d.返回并退出 \n",j+1);
printf("请从上述选项中选择,\n");
scanf("%d",&i); /*输入选项 */
}
while (i<0||i>n+1);
return (i-1);
}
程序接上页
/**********************************************************\
添加信息函数
\**********************************************************/
int AddList(int start,char Unit[][20],char Number[][20],
char Name[][20],int Parameter[][5])
{
system("cls"); /*通过 system()函数调用 dos命令,清屏 */
int i,n;
for (n=start;n<N;n++)
{
printf("\n 请输入单位 (按 e键返回 ):\n"); /*若输入 e则结束数据输入 */
scanf("%s",Unit[n]);
if (strcmp(Unit[n],"e")==0) break;
printf("\n 请输入会员编号,\n");
scanf("%s",Number[n]);
printf("\n 请输入会员姓名,\n");
scanf("%s",Name[n]);
程序接上页
for(i=0;i<5;i++) /*输入数据信息 */
{
printf("生理指标 [%d]=",i+1);
scanf("%d",&Parameter[n][i]);
}
}
return n;
// system("cls");
}
程序接上页
/**********************************************************\
显示信息函数
\**********************************************************/
void Display(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int start,int end)
{
system("cls");
int i,j;
printf("\n%18s %10s %8s ","单位 ","会员编号 ","姓名 "); /*显示标题 */
for(i=0;i<5;i++) printf("%6s%d","指标 ",i+1);
printf("\n------------------------------------------\n");
for(i=start;i<=end;i++) /*显示数据信息 */
{
printf("%18s %10s %8s",x[i],y[i],z[i]);
for (j=0;j<5;j++)
printf("%7d",Parameter[i][j]);
printf("\n");
}
}
程序接上页
/**********************************************************\
字符串互换函数
\**********************************************************/
void SwapChar(char *x,char *y)
{
char temp[20];
strcpy(temp,x);
strcpy(x,y);
strcpy(y,temp);
}
/**********************************************************\
整型数互换函数
\**********************************************************/
void SwapInt(int *x,int *y)
{
int p;
p=*x;
*x=*y;
*y=p;
}
程序接上页
/**********************************************************\
按姓名字典顺序排序函数
\**********************************************************/
void Sort(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n)
{
int i,j,k;
for (i=0;i<n-1;i++)
{
k=i;
for (j=i+1;j<n;j++)
if(strcmp(z[k],z[j])>0) k=j;
if(i!=k)
{
SwapChar(x[k],x[i]); /*交换会员标识 */
SwapChar(y[k],y[i]);
SwapChar(z[k],z[i]);
for(j=0;j<5;j++) /*交换会员数据信息 */
SwapInt(&Parameter[k][j],&Parameter[i][j]);
}
}
printf("\n 按姓名排序已完成 !\n");
}
程序接上页
/**********************************************************\
显示信息函数,为下一函数的中间调用
\**********************************************************/
int Search(char (*x)[20],char (*y)[20],char (*z)[20],char *person,int n)
{
int i;
for (i=0;i<n;i++)
if(!strcmp(z[i],person)) /*将输入信息与已有信息比较 */
return i;
return -1;
}
程序接上页
/**********************************************************\
显示信息函数
\**********************************************************/
int FindStaff(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n)
{
system("cls");
int i;
char str[20];
printf("\n 请输入要检索的会员姓名,");
scanf("%s",str);
i=Search(x,y,z,str,n); /*查找 str是否在所选数组中 */
if(i==-1)
printf("没有关于 %s 的记录,请确认无误后再次输入 !\n",str);
else
Display(x,y,z,Parameter,i,i); /*显示找到的会员信息
*/
// printf("请按任意键返回操作界面,\n");
getchar();
return i;
}
程序接上页
/**********************************************************\
信息修改函数
\**********************************************************/
void Modify(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n)
{
// system("cls");
int i,j;
i=FindStaff(x,y,z,Parameter,n); /*寻找需要修改的会员信息 */
if(i>=0) /*若找到,则进行修改 */
{
printf("请输入修改后的会员信息,\n");
printf("\n 请输入单位,\n");
scanf("%s",x[i]);
printf("\n 请输入会员编号,\n");
scanf("%s",y[i]);
printf("\n 请输入会员姓名,\n");
scanf("%s",z[i]);
for(j=0;j<5;j++) /*输入修改后的数据信息 */
{
printf("生理指标 [%d]=",j+1);
scanf("%d",&Parameter[i][j]);
}
printf("\n修改已完成 !\n");
}
}
程序接上页
/**********************************************************\
信息删除函数
\**********************************************************/
int Delete(char (*x)[20],char (*y)[20],char (*z)[20],int Parameter[][5],int n)
{
// system("cls"); /*通过 system()函数调用 dos命令,清屏 */
int i,j;
i=FindStaff(x,y,z,Parameter,n); /*寻找需要删除的会员信息 */
if(i>=0) /*若找到,则删除 */
{
strcpy(x[i],x[n-1]);
strcpy(y[i],y[n-1]);
strcpy(z[i],z[n-1]);
for(j=0;j<5;j++)
Parameter[i][j]=Parameter[n-1][j]; /*将最后一条记录覆盖当前记录 */
n=n-1; /*记录数减少 1*/
printf("\n已经删除所选会员信息 !\n"); /*如果此时调用 Display()函数,*/
} /*则显示删除后的结果 */
return n;
}
运行结果运行结果运行结果运行结果运行结果运行结果
7.1 函数概述
7.2 函数的定义
7.3 函数的声明和调用
7.4 参数的传递
7.5 嵌套调用和递归调用
7.6 指针与函数
7.7 数组作为函数参数
7.8 变量的作用域和存储类别
7.9 外部函数和内部函数
7.10 命令行参数
7.11 实例演示:“极限俱乐部会员信息系统”程序
7.1概述
为了使程序大而不繁,简洁明了,程序设计者要根据软件的总体要求,把相同功能或相似功能的操作归纳成模块的形式,并设计成函数,以实现程序设计的模块化。所以函数是 C程序的重要成分,它封装了完成某一特定功能的程序代码和数据,实现较高级的抽象。除函数的具体设计者外,其他人员只要运用已经设计好的函数,
知道函数原型,懂得如何调用,即知道函数的对外接口,
无须知道函数内部的具体实现细节,这有利于提高程序的开发效率,同时增强程序的可靠性。
7.1函数概述
C语言不仅提供了极为丰富的库函数 (如 Turbo C,MS
C都提供了三百多个库函数 ),还允许用户建立自己定义的函数。在功能上,由主函数调用其他函数,其他函数也可以互相调用,
C语言的函数作为一个模块应具备下面两个原则:
界面清晰。函数的处理子任务明确,函数之间数据传递越少越好。
大小适中。若函数太大,处理任务复杂,导致结构复杂,
程序可读性较差;反之,若函数太小,则程序调用关系复杂,这样会降低程序的效率 。
函数分类
在 C语言中,函数可分为两类,一类是由系统定义的标准函数,又称为库函数,其函数声明一般是在系统的include目录下以,h为后缀的头文件中,如在程序中要用到某个库函数,必须在调用该函数之前用 #include<头文件名 >命令将库函数信息包含到本程序中。
另一类函数是自定义函数,这类函数是根据问题的特殊要求而设计的,自定义的函数为构架复杂的大程序提供了方便,同时对程序的维护和扩充也带来了一些便利。对于自定义函数,在程序中可表现为以下两种形式。
第一种:函数声明、函数调用、函数定义。
第二种:函数定义、函数调用。
函数调用
一个 C程序由主函数和若干个或 0个用户函数组成。 C语言中的函数没有隶属关系,即所有的函数都是独立定义的,不能嵌套定义,函数是通过调用来执行的,允许函数间互相调用,也允许直接或间接的递归调用其自身。
main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。
调用另一个函数的函数称为主调函数,被调用的函数称为被调函数。一个函数调用另一个函数时是将流程控制转到被调函数,被调函数执行完后返回主调函数的调用处继续主调函数的执行。
7.2 函数的定义
调用函数必须遵循“定义在先、使用在后”的原则。函数定义的一般格式,
类型说明 函数名 (类型说明符 形参变量 1,类型说明符形参变量 2,…)
{
语句部分
}
1.函数头
包括函数名、函数的类型及形式参数表。
( 1)函数名
用标识符表示,用来标识一个函数的名字,函数名后面必须有一对圆括号。除 main函数外,其他函数可以按标识符规则任意命名,程序风格要求函数命名是能反映函数功能、有助于记忆的标识符。
( 2)函数类型
函数名前的函数类型是指函数返回值的类型。如为整型 int可省略不写;如无返回值,以 void类型明示。如果函数为无参函数,
而且在调用后没有返回值。在这种情况下,C语言规定应当将函数类型说明为 void型 (空值类型 ),否则,可能导致程序出错。
( 3)形式参数表
圆括号内为形式参数表列部分,其中的参数称为形式参数,它包括函数的自变量部分。每个参数前都应用相应的类型标识符对参数进行说明,有多个参数时用逗号分开。如果是无参数函数,形式参数表列部分为空,但一对 ()不能省略。
2.函数体
函数体是函数头下面最外层一对花括号内的代码,它是由一系列语句构成的,用以实现函数的功能,函数体内可以有函数说明、变量说明以及可执行语句。
函数体也可以是一对空的花括号,例如:
void dummy(void)
{
}
这是一个“空函数”,调用它并不产生任何有效的操作,
但却是一个符合 C语言语法的合法函数。在程序开发过程中,通常先开发主要函数,一些次要的函数或有待以后扩充和完善功能的函数暂时写成空函数,使程序可以在不完整情况下调试部分功能。当函数执行到 return语句或执行完函数体所有语句,流程就回到主调函数。
关于 return语句说明如下:
return语句是函数的逻辑结尾,不一定是函数的最后一条语句,一个函数中允许出现多个 return语句,但每次只能有一个 return语句被执行。
如果不需要从被调函数带回返回值可以不要 return语句。
一般情况是将函数类型定义为 void型,也叫空类型,此种类型的函数一般用来完成某种操作例如输出程序运行结果等。
还可以用不带表达式的 reture作为函数和逻辑结尾,这时,reture的作用是将控制权交给调用函数,而不是返回一个值。
7.3 函数的说明和调用
不同的函数实现各自的功能,完成各自的任务 。 要将它们组织起来,按一定顺序执行,是通过函数调用来实现的 。
主调函数通过函数调用向被凋函数进行数据传送,控制转移;
被凋函数在完成自己的任务后,又会将结果数据回传给主调函数并交回控制权 。
各函数之间就是这样在不同时间,情况下实行有序的调用,共同来完成程序规定的任务 。
如果一个源程序包含了多个函数,而函数间又有相互调用,那将会形成一种复杂的局面,使,定义在前,使用在后,的原则难以实现 。 为此,C语言通过函数声明语句解决这个问题 。
7.3.1 函数的声明
函数声明语句的般格式:
类型说明符 函数名 (类型说明符 形参变量 1,类型说明符 形参变量 2,…);
从形式上看,函数声明就是在函数定义格式的基础上去掉了函数体。通常,将函数声明安排在源文件的开始部分。函数声明中的形参变量名可以省略。
函数声明是语句,所以最后的结束符,;”不可缺少。使用函数声明后,可以将函数的定义放在源程序的后部。
函数说明通常出现在程序的开头,第一个函数定义之前,
也可放在主调函数的开头。有了函数说明,编译系统就对函数每次调用进行检查,将函数说明和函数调用进行对比,以保证调用时使用的参数、类型、返回值类型都是正确的。
7.3.2 函数的调用
一个函数写好后,若不通过函数调用,是不发挥任何作用的,
函数调用是通过函数调用语句来实现的 。 函数调用时会去执行函数语句中的内容,函数执行完毕后,回到函数的调用处,
继续执行下面的语句 。
函数调用有三种方式:
( 1)函数语句
把一个函数作为一个语句。如,dy();”。这些不要求函数返回值,只要求函数完成一定的操作。
( 2)函数表达式
函数出现在一个表达式中,这种表达式称为函数表达式,这时要求函返回一个确定的值以参加表达式的运算。如
,c=ftoc(f);”。
( 3)函数参数
函数的调用作为一个函数的参数,这时要求函数返回一个确定的值作为另一个函数的实际参数。如
,m=max(max(x,y),z);”。
在编写程序、进行函数调用时,注意几点:
C语言参数传递时,主调函数中实参向被调函数中形参的数据传送一般采用传值方式,把各个实参值分别顺序对应地传给形参。被调函数执行中形参值的变化不会影响主调函数中实参变量的值。但数组名作为参数传送时不同,它是“传址”,会对主调函数中的数组元素产生影响。
由于采用传值方式,实参表列中参数允许为表达式及常量。尤其值得注意的是,对实参表达式求值,C语言并没有规定求值的顺序。采用自右至左或自左至右顺序的系统均有。许多 C(Turbo C,MS C)是采用自右至左的顺序求值的。遇此情形,为保证函数调用能得到正确的执行结果,编写程序时应尽量采用其他可行的办去,加以避免为好。
在编写程序、进行函数调用时,注意几点:
注意采用函数原型声明对被调函数参数类型进行说明。
如不作说明,C语言无法进行实参类型的检查与转换。
稍有疏忽,会造成实参个数、类型与形参个数、类型不符,将引起参数传送出错,导致运算结果的大相径庭。
函数调用也是一种表达式,其值就是函数的返回值。
函数调用时,被调用函数可以是库函数或用户自定义的函数。如果是库函数,一定要在本文件的开头加上头文件。
7.4 参数的传递
7.4.1 普通值传递
值传递方式所传递的是参数值 。 调用函数时,将实参的值计算出来传递给对应的形参 。
说明:
( 1)实参可以是常量或表达式,也可以是函数调用语句,
如以下函数调用,num_of_primes(4,1000)、
num_of_primes(5,x*y),abs_sum(abs_sum(x,y),z)等,
都是正确的。
( 2)实参与形参不共用存储单元,即使同名,形参值的改变也不会影响主凋函数实参变量的值。
( 3)实参与形参的类型应该匹配,由实参将数据传送给形参。
( 4)对于有返回值的函数,调用时若没有把它赋给某个变量,仍然是可以的,只是函数的返回值有可能会被丢失。
7.4.2 地址值传递
地址值传递指的是调用时给出的实参是变量的地址值,
此时函数参数 (形参 )应该是指针变量。
形参指针得到某变量的地址值后,形参指针就直接指向该变量,所以在执行函数体的过程中形参指针通过间接访问可以改变这个变量的值,这是地址值传递的特点,
利用这一点,在需要时调用函数后可以“返回”多个值。
但是地址值的调用还是传值调用,此时传的是地址值,
其传值的单向性没有改变,即形参指针在函数执行过程中的改变不影响实参 (地址值 ).
7.5 嵌套调用和递归调用
7.5.1 嵌套调用
函数的嵌套调用指的是函数调用函数,实际上前面的程序中都有主函数调用常用的输入输出函数,或调用数学函数,或调用字符串函数,或调用自定义函数的例子,
只不过是主函数在调用它们而已,从广义来说也是嵌套调用。
函数的嵌套调用更多的是指函数的连环调用。
函数嵌套调用过程中,每次调用函数时系统都要开辟一个新的内存区域用来存放被调用函数的代码和记录当时调用函数的状态和返回地址等。所以嵌套调用层次越多,
时空开销越大 (即运行时间长,占用内存空间多 )。
7.5.2 递归调用
一个函数在它的函数体内调用它自身称为递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层。
C语言中允许在函数中调用函数自身,或函数之间相互调用,这种调用方式称之为递归。根据不同的调用方式,
递归又分为直接递归调用和间接递归调用。
直接递归调用:函数直接调用自身。
间接递归调用:函数互相调用对方。
在递归函数的调用中,问题的求解分为两个阶段:第一阶段是“回推”,即从所要求的问题回推到递归的结束条件处 ;第二阶段是“递推”,即再从递归的结束条件递推到要求的问题,这时才可以求出结果。
7.6 指针与函数
7.6.1指针变量作函数参数
#include<stdio.h>
void swap(int,int);
void main()
{ int a=5,b=9;
printf("%d,%d\n",a,b);
swap(a,b);
printf("%d,%d\n",a,b);}
void swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;}
运行结果:
7.6.2 返回值为指针值的函数
#include<stdio.h>
int larger1(int,int);
int *larger2(int*,int*);
void main()
{ int a,b,bigger1,*bigger2;
printf("input two interger numbers,");
scanf("%d,%d",&a,&b);
bigger1=larger1(a,b); /*函数 larger1返回指向变量的值 */
printf("The larger number is %d\n",bigger1);
bigger2=larger2(&a,&b); /*函数 1argcr2返回指向整型变量的指针值 */
printf("The larger number is %d\n",*bigger2);}
int larger1(int x,int y)
{ return x>y?x:y;}
int *larger2(int*x,int*y)
{ return *x>*y?x:y;}
运行结果为:
7.6.3 函数的指针和指向函数的指针变量
定义一个指针变量,让它指向某个函数,这个变量就称为指向函数的指针变量。利用指向函数的指针变量的作用是更灵活地进行函数调用。函数指针的定义格式如下:
数据类型 (*函数指针变量名 )();
说明:
(1)在定义函数指针时,使用圆括号括起函数指针变量名。
(2)其中的“数据类型”是指函数返回值的类型。
阅读下面的程序,了解函数指针的使用
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
void main()
{
int(*p)(int,int);
p=max;
printf("The max of 3 and 4 is %d\n",(*p)(3,4));
}
程序运行结果:
7.7 数组作为函数参数
数组用作函数参数有两种形式,一种是把数组元素 (下标变量 )作为实参使用;另一种是把数组名作为函数的形参和实参使用。
用数组名作函数参数时,应该注意如下几个方面
①数组名作为函数参数,应该在主调函数与被调函数中分别定义数组,不能只在一方定义。
②要注意用数组元素作实参和数组名作实参的区别:用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。
而用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。
用数组名作函数参数时,应该注意如下几个方面
③ 实参数组和形参数组大小不要求一致,因为传送时只是将实参数组的首地址传给形参数组。因此,一维形参数组也可以不指定大小,在定义数组时,在数组名后跟一个空的方括弧。
被调函数涉及对数组元素的处理,可另设一个参数来指明数组元素的个数。
④数组名作为函数参数,传送整个数组数据,不是用实参数组与形参数组元素间的“传值”方式,而是用把实参数组的首地址传给形参数组的“传址”方式。形参数组不会被分配内存空间,它与实参数组共用一段内存空间。在“传址”方式下,由于共用一段内存空间,形参数组中各元素值的变化直接影响到实参数组元素值的变化。
⑤ 多维数组名也可以作为实参和形参,使用时,也应该在主调函数与被调函数中分别定义数组,数组类型也应该一致,这样才不致出错。另外,在被调函数中对形参数组定义时,至多也只能省略第一维的大小说明。这是因为多维数组的数组元素是按连续地址存放的,不给出各维的长度说明,将无法判定数组元素的存储地址。也不能只指定第一维,不指定第二维以后的大小 (因实参、
形参数组可以大小不一致 )。
用数组名作函数参数时,应该注意如下几个方面
7.8变量的作用域和存储类别
7.8.1 局部变量和全局变量
1.局部变量
不管是主函数还是其他函数,在函数内说明的变量 (包括函数参数 )统称为局部变量。
在没有其他修饰符的情况下,局部变量的特点是随函数的调用而生成,随函数调用结束而释放。
形参变量只有在函数内才是有效的,离开该函数就不能再使用了。
程序中的大多数变量是局部变量。不同的函数在说明局部变量时可能会同名,这不会引起混淆或冲突,因为它们各自只在自己的函数内有效。
局部变量小结:
在主函数 main()中定义的局部变量,只能在主函数中使用,其他函数不能使用。同时,主函数中也不能使用其他函数中定义的局部变量。因为主函数也是一个函数,
与其他函数在定义上是平行关系,应予以注意。
形参变量是局部变量,属于被调用函数。实参变量是属于主调函数的局部变量。
允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会混淆。
在复合语句中也可定义变量,其作用域只在复合语句范围内。
2.全局变量
假如变量是定义在函数外面 (它们不属于任何一个函数 ),
那么,这些变量自定义开始就存在了,一直到程序运行结束。定义在函数外面的变量统称全局变量,自定义开始,它后面的任何函数都可以访问它 (条件是函数内没有同名的局部变量或定义变量时没有其他修饰符 )。全局变量的这个特点在程序中既带来方便,也带来“隐患”,因为它破坏了模块的封装性和数据的安全性,由此建议限量应用。
全局变量优点
增加了各函数间数据传送的渠道。特别是函数返回值通常仅限于一个,这在很多场合不能满足使用要求。此时利用全局变量,可以得到更多的数据处理结果。
利用全局变量可以减少函数实参与形参的个数。其带来的好处是减少函数调用时分配的内存空间以及数据传送所必需的传送时间。
全局变量的缺点
全局变量的作用范围大,为此必然要付出的代价是其占用存储单元时间长。它在程序的全部执行过程都占据着存储单元,不像局部变量那样仅在一个函数的执行过程中临时占用存储单元。
函数过多使用全局变量,降低了函数使用的通用性。通过外部变量传递数据也增加了函数间的相互影响,函数的独立性、封闭性、可移植性大大降低,出错的几率增大。
多人合作完成的程序通常由多个源文件组成。一旦出现全局变量同名的情况,将引起程序出错。
使用全局变量过多,会降低程序的清晰度,使人难以判断某一时刻各个全局变量的值,因为各个函数在执行时都可以改变全局变量的值。
7.8.2变量的存储类别
变量占用内存单元的时间称为 "生存期 ",变量的生存期是由变量的存储位置决定的。程序中使用的数据可存放在 CPU的寄存器和内存储器中。
CPU寄存器,CPU寄存器中存储的数据是动态存储类型,
不能长期占用。
内存:内存中又分为两个区域,即静态存储区和动态存储区。其中,静态存储区存放的数据可以在程序执行期间长期占有内存单元,直到该程序结束;动态存储区存放的数据是临时占用内存单元,当某一程序段执行完毕,
系统收回内存单元。
数据的存储类型及存储地点存储类型 存储类型符 存储地点自动型 auto 内存动态存储区寄存器型 register CPU寄存器静态型 static 静态存储区外部变量 Extern 静态存储区不同存储类别的变量:
( 1)自动变量 (auto)
存储在堆区和栈区的变量,随函数调用而生成,随函数调用结束而释放,C语言从存储的角度称这些变量为自动变量,
( 2)静态变量
静态变量存放在静态存储区中。静态变量在变量定义时就分配了固定的内存单元,并根据所定义的数据类型存入默认值,在程序运行中一直占用内存单元不释放,直到程序运行结束才释放。
静态变量
静态变量又分为静态局部变量和静态全局变量。
静态局部变量
在函数或复合语句中定义的静态变量,称为静态局部变量,该变量在其函数中或复合语句中有效。静态局部变量所在的函数无论调用多少次,静态局部变量赋值语句只执行一次,但能够保留住函数多次调用的中间结果,
这就相对保持了程序的独立性。
静态全局变量
在函数体外定义的变量称为全局变量。使用关键字
"static"定义的全局变量称为静态全局变量,该变量只在本文件中有效,其他的文件不可以使用此变量 (这一点要与外部变量加以区分 )。
( 3)外部变量 (extern)
C语言允许将一个源文件程序清单分放在若干个程序文件中 (若干个,c文件 ),采用分块编译方法编译生成一个目标程序 (,obj文件 )。其中每个程序称为一个 "编译单位 ",最后,将它们连接在一起生成,exe文件,从而达到提高编译速度和便于管理大型软件工程的目的。Tubro C系统规定在某一个源程序中定义的全局变量,
其他的多个文件可以使用,这就是所说的程序间的数据交流,在 C语言中称为外部变量。
外部变量使用关键字 "extern"定义,在函数体之外定义,
存放在静态存储区。
( 4)寄存器变量 (register)
有一定计算机基础的读者都知道计算机内部寄存器的存取速度比内存中存储单元的存取速度快得多。为了提高某些经常使用变量的存取速度,Turbo C语言提供了寄存器变量。这种变量使用的是 CPU内的寄存器,不需要通过访问内存来确定和修改其数值。寄存器变量与一般自动变量相比,它可以提高存取速度一个数量级或更多。
其定义格式为:
register类型名 变量名 ;
7.9外部函数和内部函数
7.9.1 内部函数
如果在一个源文件中定义的函数只能被本文件中的函数调用,而不能被其他文件中的函数调用,这种函数称为内部函数。使用保留字 static定义内部函数,其格式为:
static数据类型 函数名 (形参表 );
7.9.2 外部函数
前面定义的函数可以说都是外部函数,它们可以被本文件内调用,也可以被外部文件 (当程序是由多文件组成时 )调用。但是在外部文件调用时,外部文件在调用前需做如下说明:
extern数据类型 函数名 (形参表 );
7.10命令行参数
在 C语言中,main函数也可以带参数,参数的个数最多为 3个,但参数名及参数的顺序和类型是固定的,形式如下:
main(int argc,char *argv[],char *env[])
根据 C语言的规则,main函数的形式可有如下几种:
① main()
② main(int argc)
③ main(int argc,char*argv[])
④ main(int argc,char*argv[],char*env[])
其中第②种情况不常用。
C源程序的特点:
程序一般用小写字母书写;
大多数语句结尾必须要用 ";"作为终止符,否则 C不认为该语句结束;
每个程序必须有一个而且只能有一个称作主函数的
main()函数;
每个程序体(主函数和每个子函数,如上例中的 main()
函数和 sub()函数)必须用一对花括号 "{"和 "}"括起来;
一个较完整的程序大致包括,包含文件 (一组
#include<*.h>语句 )、用户函数说明部分、全程变量定义、主函数和若干子函数组成。在主函数和子函数中又包括局部变量定义、若干个 C库函数、控制流程语句、
用户函数的调用语句等;
注释部分包含在 "/*"和 "*/"之间,在编译时它被 C编译器忽略。
说明:
象其它一些语言一样,C的变量在使用之前必须先定义其数据类型,未经定义的变量不能使用。定义变量类型应在可执行语句前面。
C中,大、小写字母是有区别的,相同字母的大、小写代表不同的变量。
C程序的书写格式非常灵活,没有严格限制。
7.11实例演示:“极限俱乐部会员信息系统”程序
/*系统密码,123321*/
/*极限俱乐部会员信息系统 */
#include "stdio.h" /*控制台 I/O的头文件 */
#include "string.h" /*字符串函数的头文件 */
#include "stdlib.h" /*exit()函数的头文件 */
#define M 20 /*定义最多可录入的单位数 */
#define N 100 /*定义最多可录入的会员数 */
int Interface(char (*x)[20],int n); /*操作界面函数 */
int AddList(int start,char Unit[][20],char Name[][20],
char Number[][20],int Parameter[][5]); /*添加信息函数 */
void Display(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int start,int end); /*显示信息函数 */
程序接上页
void SwapChar(char *x,char *y); /*字符互换函数 */
void SwapeInt(int *x,int *y); /*整型数互换函数 */
void Sort(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*按姓名排序函数 */
int Search (char (*x)[20],char (*y)[20],char (*z)[20],
char *person,int n); /*信息检索函数 */
int FindStaff(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*信息查找函数 */
void Modify(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*信息修改函数 */
int Delete(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n); /*信息删除函数 */
char password[8]="7654321"; /*/设置系统管理员密码 *
程序接上页
/**********************************************************\
主函数
\**********************************************************/
void main()
{
char Unit[M][20]; /*单位 */
char Number[N][20]; /*编号 */
char Name[N][20]; /*姓名 */
int Parameter[N][5]; /*成绩 */
int select;
int n=0;
char mainName[][20]={"添加信息 ","显示信息 ","按姓名排序 ",
"按姓名检索 ","修改信息 ","删除信息 "}; /*系统菜单
*/
程序接上页
/**********************系统密码管理 ********************/
char choice;
char s[8];
int flag = 0;
int w = 3; /*设置输入密码最多允许连续出错的次数
*/
do
{
printf("请输入管理员密码,\n");
scanf("%s",s);
if(!strcmp(s,password)) /*验证密码 */
{
printf("PASS\n\n\n"); /*密码正确,则输出,pass”*/
flag = 1;
break;
}
程序接上页
else
{
printf("密码错误,请重新输入,\n");
w--;
}
}
while(w>0);
if(!flag)
{
printf("你已连续三次输入错误,请确认后再使用本系统,谢谢 !\n");
exit(0);
}
system("cls"); /*用 system调用 dos命令,清屏 */
程序接上页
/*************************欢迎界面 ************************************/
printf("~~~~~~~~~~~~~\t\t\t\t\t~~~~~~~~~~~~~\n");
printf("\t\t欢迎进入极限俱乐部会员信息系统 \n"); /*标题 */
printf("\n%%%%%%%%%%%%%%%%%%%%%%%%%\t\t\t\t\t%%%%
%%%%%%%%%%%%%%%%%%%%%%%\n");
printf("\n\n\n\n");
printf("单位,北京极限俱乐部 \n"); /*用户单位 */
printf("地址北京市西城区西四 \n");
printf("会员热线,010-66886688");
printf("\n\n\n\n");
printf("==============\t\t==============\n");
printf("本系统由“天地人和软件有限公司”开发 \n"); /*开发商 */
printf("作者:天天 \n");
printf("地址:北京市海淀区学院路 \n");
printf("==============\t\t==============\n");
printf("\n\n");
printf("\t\t请任意输入一个值并回车进入系统 \n");
scanf("%s",&choice);
if(choice== '\n'||choice== 'N')
exit(1);
程序接上页
/***********************欢迎界面结束 ******************************/
system("cls"); /*通过 system()函数调用 dos命令,清屏 */
do
{
select=Interface(mainName,6);
switch (select) /*转向语句,选择以实现不同的功能 */
{
case 0,n=AddList(n,Unit,Number,Name,Parameter);break;
case 1,Display(Unit,Number,Name,Parameter,0,n-1);break;
case 2,Sort(Unit,Number,Name,Parameter,n);break;
case 3,FindStaff(Unit,Number,Name,Parameter,n);break;
case 4,Modify(Unit,Number,Name,Parameter,n);break;
case 5,n=Delete(Unit,Number,Name,Parameter,n);break;
}
}
while(select>=0&&select<=5);
}
程序接上页
/**********************************************************\
操作界面函数
\**********************************************************/
int Interface(char (*x)[20],int n)
{
int i=0;
int j;
printf("\n××××××× 极限俱乐部会员信息系统 ××××××× \n");
do
{
for (j=0;j<n;j++)
printf("%2d.%s\n",j+1,x[j]);
printf("%2d.返回并退出 \n",j+1);
printf("请从上述选项中选择,\n");
scanf("%d",&i); /*输入选项 */
}
while (i<0||i>n+1);
return (i-1);
}
程序接上页
/**********************************************************\
添加信息函数
\**********************************************************/
int AddList(int start,char Unit[][20],char Number[][20],
char Name[][20],int Parameter[][5])
{
system("cls"); /*通过 system()函数调用 dos命令,清屏 */
int i,n;
for (n=start;n<N;n++)
{
printf("\n 请输入单位 (按 e键返回 ):\n"); /*若输入 e则结束数据输入 */
scanf("%s",Unit[n]);
if (strcmp(Unit[n],"e")==0) break;
printf("\n 请输入会员编号,\n");
scanf("%s",Number[n]);
printf("\n 请输入会员姓名,\n");
scanf("%s",Name[n]);
程序接上页
for(i=0;i<5;i++) /*输入数据信息 */
{
printf("生理指标 [%d]=",i+1);
scanf("%d",&Parameter[n][i]);
}
}
return n;
// system("cls");
}
程序接上页
/**********************************************************\
显示信息函数
\**********************************************************/
void Display(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int start,int end)
{
system("cls");
int i,j;
printf("\n%18s %10s %8s ","单位 ","会员编号 ","姓名 "); /*显示标题 */
for(i=0;i<5;i++) printf("%6s%d","指标 ",i+1);
printf("\n------------------------------------------\n");
for(i=start;i<=end;i++) /*显示数据信息 */
{
printf("%18s %10s %8s",x[i],y[i],z[i]);
for (j=0;j<5;j++)
printf("%7d",Parameter[i][j]);
printf("\n");
}
}
程序接上页
/**********************************************************\
字符串互换函数
\**********************************************************/
void SwapChar(char *x,char *y)
{
char temp[20];
strcpy(temp,x);
strcpy(x,y);
strcpy(y,temp);
}
/**********************************************************\
整型数互换函数
\**********************************************************/
void SwapInt(int *x,int *y)
{
int p;
p=*x;
*x=*y;
*y=p;
}
程序接上页
/**********************************************************\
按姓名字典顺序排序函数
\**********************************************************/
void Sort(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n)
{
int i,j,k;
for (i=0;i<n-1;i++)
{
k=i;
for (j=i+1;j<n;j++)
if(strcmp(z[k],z[j])>0) k=j;
if(i!=k)
{
SwapChar(x[k],x[i]); /*交换会员标识 */
SwapChar(y[k],y[i]);
SwapChar(z[k],z[i]);
for(j=0;j<5;j++) /*交换会员数据信息 */
SwapInt(&Parameter[k][j],&Parameter[i][j]);
}
}
printf("\n 按姓名排序已完成 !\n");
}
程序接上页
/**********************************************************\
显示信息函数,为下一函数的中间调用
\**********************************************************/
int Search(char (*x)[20],char (*y)[20],char (*z)[20],char *person,int n)
{
int i;
for (i=0;i<n;i++)
if(!strcmp(z[i],person)) /*将输入信息与已有信息比较 */
return i;
return -1;
}
程序接上页
/**********************************************************\
显示信息函数
\**********************************************************/
int FindStaff(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n)
{
system("cls");
int i;
char str[20];
printf("\n 请输入要检索的会员姓名,");
scanf("%s",str);
i=Search(x,y,z,str,n); /*查找 str是否在所选数组中 */
if(i==-1)
printf("没有关于 %s 的记录,请确认无误后再次输入 !\n",str);
else
Display(x,y,z,Parameter,i,i); /*显示找到的会员信息
*/
// printf("请按任意键返回操作界面,\n");
getchar();
return i;
}
程序接上页
/**********************************************************\
信息修改函数
\**********************************************************/
void Modify(char (*x)[20],char (*y)[20],char (*z)[20],
int Parameter[][5],int n)
{
// system("cls");
int i,j;
i=FindStaff(x,y,z,Parameter,n); /*寻找需要修改的会员信息 */
if(i>=0) /*若找到,则进行修改 */
{
printf("请输入修改后的会员信息,\n");
printf("\n 请输入单位,\n");
scanf("%s",x[i]);
printf("\n 请输入会员编号,\n");
scanf("%s",y[i]);
printf("\n 请输入会员姓名,\n");
scanf("%s",z[i]);
for(j=0;j<5;j++) /*输入修改后的数据信息 */
{
printf("生理指标 [%d]=",j+1);
scanf("%d",&Parameter[i][j]);
}
printf("\n修改已完成 !\n");
}
}
程序接上页
/**********************************************************\
信息删除函数
\**********************************************************/
int Delete(char (*x)[20],char (*y)[20],char (*z)[20],int Parameter[][5],int n)
{
// system("cls"); /*通过 system()函数调用 dos命令,清屏 */
int i,j;
i=FindStaff(x,y,z,Parameter,n); /*寻找需要删除的会员信息 */
if(i>=0) /*若找到,则删除 */
{
strcpy(x[i],x[n-1]);
strcpy(y[i],y[n-1]);
strcpy(z[i],z[n-1]);
for(j=0;j<5;j++)
Parameter[i][j]=Parameter[n-1][j]; /*将最后一条记录覆盖当前记录 */
n=n-1; /*记录数减少 1*/
printf("\n已经删除所选会员信息 !\n"); /*如果此时调用 Display()函数,*/
} /*则显示删除后的结果 */
return n;
}
运行结果运行结果运行结果运行结果运行结果运行结果