C语言--函数

原有的编程实例已经独立为一篇博客!C语言程序设计——小学生口算刷题系统

函数的定义

函数是一个完成特定工作的独立程序模块,包括库函数和自定义函数两种。例如,scanf()、printf()等为库函数,由C语言系统提供定义,编程时只要直接调用即可;而有时,我们需要一些能实现特定功能的函数,这时就需要用户自己定义,属于自定义函数。从函数实现计算功能角度来看,C语言的函数与数学上的函数概念十分接近。在C程序中必然为某一种数据类型,称其为函数类型。
函数定义的一般形式为:

函数类型 函数名(形式参数表)   /*函数首部*/
{
函数实现过程   /*函数体*/
}

函数首部

函数首部由函数类型、函数名和形式参数表(以下简称形参表)组成,位于函数义的第一行。函数首部中,函数名是函数整体的称谓,需用一个合法的标识符表示。函数类型指函数结果返回的类型,一般与return语句中表达式的类型一致。形参表中给出函数计算所要用到的相关已知条件,以类似变量定义的形式给出,其格式为:
类型形参1,类型2 形参2,…,类型n形参n
 形参表中各个形参之间用逗号分隔,每个形参前面的类型必须分别写明。函数的 参的数量可以是一个,也可以是多个,或者没有形参。

  • 函数首部后面不能加分号,它和函数体一起构成完整的函数定义。

函数体

函数体体现函数的实现过程,由一对大括号内的若干条语句组成,用于计算,或完成特定的工作,并用return语句返回运算的结果。

函数的调用

定义一个函数后,就可以在程序中调用这个函数。在C语言中,调用标准库函数时.只需要在程序的最前面用include命令包含相应的头文件;调用自定义函数时,程序中必须有与调用函数相对应的函数定义。作为初学者的我们,充分理解函数调用与返回的实现过程,对学好函数程序设计是至关重要的。

函数调用过程

任何C程序执行,首先从主函数main()开始,如果遇到某个函数调用,主函数将被暂停执行,转而执行相应的函数,该函数执行完后将返回主函数,然后再从原先暂停的位置继续执行。

函数调用的形式

函数调用的一般格式为:

函数名(实际参数表)

实际参数(简称实参)可以是常量、变量和表达式作为实参,对于实现计算功能的函数,函数调用通常出现在赋值语句或输出函数的实参。

参数传递

函数定义时,位于其首部的参数被称为形参,主调函数的参数被称为实参。形参除了能接受实参的值外,使用方法与普通变量类似。形参和实参必须一一对应,两者数量相同,类型尽量一致。程序运行遇到函数调用时,实参的值依次传给形参,这就是参数传递。
函数的形参必须是变量,用于接受实参传递过来的值;而实参或表达式其作用是把常量变量或表达式的值传递给形参。如果实参是变量,它与所对应的形参是两个不同的变量。实参是主调函数的,形参是自定义函数的,这两者可以同名,也可不同名。
按照C语言的规定,在参数传递过程中,将实参的值复制给形参。这种参数传递是单向的,只允许实参把值复制给形参,形参的值即使在函数中改变了,也不会反过来影响实参。

  • 实参和形参一一对应,数量应相同,顺序应一致,初学时建议类型也保持一致。

函数结果返回

函数结果返回的形式如下:

return表达式;

先求解表达式的值,再返回其值。一般情况下表达式的类型与函数类型应一致,如果两者不一致,以函数类型为准。return语句的作用有两个:一是结束函数的运行;二是带着运算结果(表达式的值)返回主调函数。
在函数体中,return语句中的表达式反映了函数运算的结果,通过return语句将该结果回送给主调函数。但return语句只能返回一个值,如果函数产生了多个运算结果,将无法通过return返回。例如求一元二次方程的函数,就不能用return返回两个根。
在接下来的学习中,我们将会学习使用全局变量或指针实现函数多个结果返回。

  • return语句只能返回一个值。

函数原型声明

C语言要求函数先定义后调用,就像变量先定义后使用一样。如果自定义函数被放在主调函数的后面,就需要在函数调用前,加上函数原型声明(或称为函数声明)。函数声明的目的主要是说明函数的类型和参数的情况,以保证程序编译时能判断对该函数的调用是否正确。函数声明的一般格式为:

函数类型 函数名(参数表);

即与函数定义中的第一行函数首部相同,并以分号结束。

  • 函数声明是一条C语句,而函数定义时的函数首部不是语句,后面不能跟分号。

虽然可以将主调函数放在被调函数的后面,从而不需做声明。但考虑到函数的执行顺序,在编程时一般都把主函数写在最前面,使整个程序的结构和功能开门见山地呈现在读者面前,然后通过函数声明解决函数先调用后定义的矛盾。

  • 如果在调用函数前,既不定义,也不声明,程序编译时会出错。

不返回结果的函数

前面我们谈的的函数主要是是起计算或判断作用,最终有一个函数结果返回。在很多程序设计中,调用函数不是为了得到某个运算结果,而是要让它产生某些作用,具有类似作用的函数在有些语言中也称为过程。不返回结果的函数定义:

void函数名 (形参表)  /*函数首部*/
{
   函数实现过程   /*函数体*/
}

函数类型为void,表示不返回结果,函数体中可以使用没有表达式的return语句,可以省略间return。void类型的函数虽然不直接返回一个值,但它的作用通常以屏幕输出等方式体现。

  • 在不返回结果的函数定义中,void不能省略;否则,函数类型被默认定义为int。

省略了return语句,并不意味着函数不能返回。对于void类型的函数,如果省略了return语句,当函数中所有语句都执行完后,遇到最后的大括号即自动返回主调函数。
由于函数没有返回结果,函数调用不可能出现在表达式中,通常以独立的调用语句。
不返回结果的函数在定义、调用、参数传递、函数声明上,思路完全与以前相同,只是函数类型变为void。它适用的场合主要是把一些确定的、相对独立的程序功能封装成函数。主函数通过调用不同的函数,体现算法步骤,而各步骤的实现由相应函数完成,从而简化主函数结构,以体现结构化程序设计思想。

局部变量和全局变量

局部变量

迄今为止,在程序中使用的变量都定义在函数内部,它们的有效使用范围被局限于所在的函数内。因此主调函数只有通过参数传递,才能把实参数据传递给函数使;同样,形参的改变也不会影响到实参变量。这种变量的有效使用范围,最大程度保了各函数之间的独立性,避免函数之间相互干扰。
C语言中把定义在函数内部的变量称为局部变量,局部变量的有效作用范围局限于所在的函数内部。形参是局部变量。
使用局部变量可以避免各个函数之间的变量相互干扰。当函数使用了同名的形参时,甚至于主函数的实参变量也同名时,由于分属不同函数,它们有各自不同的变量实体和使用范围,不会相互千扰。C语言的这个特性在结构化程序设计中非常有用。
除了作用于函数的局部变量外,C语言还允许定义作用于复合语句中的局部变量,其有效使用范围被局限于复合语句内,一般用作小范围内的临时变量。

  • 局部变量一般定义在函数或复合语句的开始处,标准C规定其不能定义在中间位置。

全局变量

局部变量虽然保证了函数的独立性,但程序设计有时还要考虑不同函数之间的数据交流,及各函数的某些统一设置。当一些变量需要被多个函数共同使用时,参数传递虽然是一个办法,但必须通过函数调用才能实现,并且函数只能返回一个结果,这会使程序设计受到很大的限制。为了解决多个函数间的变量共用,C语言允许定义全局变量。
定义在函数外而不属于任何函数的变量称为全局变量。全局变量的作用范围是从定义开始到程序所在文件的结束,它对作用范围内所有的函数都起作用。
全局变量的定义格式与局部变量完全一致,只是定义位置不在函数内,它可以定义在程序的头部,也可以定义在两个函数的中间或程序尾部,只要在函数外部即可。

  • 一般情况下把全局变量定义在程序的最前面,即第一个函数的前面。

由于全局变量和局部变量的作用范围不同,允许它们同名。当某函数的局部变量与全局变量同名时,在该函数中全局变量不起作用,而由局部变量起作用。对于其他不存在同名变量的函数,全局变量仍然有效。同样,当函数局部变量与复合语句的局部变量同名时,以复合语句为准。

  • 全局变量可以帮助解决函数多结果返回的问题,但全局变量更多地用于多函数间的全局数据表示。

思考:

我们可能认为使用全局变挺比使用局部变量自由度大,更方便。一旦定义,所有函数都可直接使用,连函数参数都可省略,甚至函数返回结果个数也不受限制,不需要使用return语句,可以直接通过全局变量回送结果。从表面上看,全局变量确实能实现这些要求,但对于规模较大的程序,过多使用全局变量会带来副作用,导致各函数间相互干扰。如果整个程序是由多人合作开发的,各人都按自己的想法使用全局变量,相互的干扰可能更严重。因此在变量使用中,应尽量使用局部变量,从某个角度看使用似乎受到了限制;但从另一个角度看,它避免了不同函数间的相互干扰,提高了程序质量。

  • 全局变量虽然可以用于多个函数之间的数据交流,但一般情况下,应尽量使用局部变量和函数参数。

静态变量

1.变量生存周期

变量是保存变化数据的工作单元,计算机用内存单元来对应实现。一旦在程序中定义变量,计算机在执行过程中就会根据变量类型分配相应的内存单元供变量保存数据。
就一般程序而言,计算机都是从主函数开始运行的,使得main函数中所有的局部变量,一开始就在内存数据区中分配了存储单元。而其他函数在被调用之前,其局部变量并未分配存储单元,只有当函数被调用时,其形参和局部变量才被分配相应存储单元;一旦函数调用结束返回主调函数,在函数中定义的所有形参和局部变量将不复存在,相应的存储单元由系统收回。根据这种特性,把局部变量称为自动变量,即函数被调用时,系统自动为其局部变量分配存储单元;一旦该函数结束(不一定是整个程序运行结束),所有分配给局部变量的单元由系统自动回收。变量从定义开始分配存储单元,到运行结束存储单元被回收,整个过程称为变量生存周期。

静态变量

在静态存储区中,除了全局变量外,还有一种特殊的局部变量一一静态局部变量。它存放在静态存储区,不会像普通局部变量那样因为函数调用结束而被系统回收,它的生存周期会持续到程序结束。由于存储单元被保留,一旦含有静态局部变量的函数被再次调用,则静态局部变量会被重新激活,上一次函数调用后的值仍然保存卷,可供本次调用继续使用。静态变量定义格式:

static 类型名 变量表

自动变量如果没有赋初值,其存储单元中将是随机值。就静态变量而言,如果定义时没有赋初值,系统将自动赋0。并且赋初值只在函数第一次调用时起作用,以后调用都按前一次调用保留的值使用。这是因为静态局部变量的生存周期始于函数的第一次调用,贯穿于整个程序。当函数第一次调用时,静态局部变量的内存单元得以分配,赋以初值,而函数被再次调用时,此静态局部变量单元已经存在,计算机不会再次为它分配单元,赋初值也不再发生。但静态局部变量受变量作用范围限制,不能作用于其他函数(包括主函数)。

  • 静态变量赋初值只在函数第一次调用时起作用,若没有赋初值,系统将自动赋值0。

静态变量与全局变量均位于静态存储区,它们的共同点是生存周期贯穿整个程序执行过程。区别在于作用范围不同,全局变量可作用于所有函数,静态变量只能用于所定义函数,而不能用于其他函数。静态变量和全局变量一样,属于变量的特殊用法,若没有静态保存的要求,不建议使用静态变量。

  • 除了静态局部变量外,C语言也宥静态全局变量,它的作用与程序文件结构有关。

函数应用


解决这个问题,我的代码一开始是这样的:

由于这一次我们是将输入的三个数组合成年月日,然后来判断合法性,根据排列组合,一共有六种可能需要我们考虑。因此我使用了穷举法,把六种可能依次搞了一遍。但是,我们看到这六种情况的代码除了变量的位置以外完全相同,直接这么写等于把一件事重复做了6遍,感觉这么写效率好低啊!我们有没有更好地方法去实现这个功能呢?

使用函数把这段代码封装起来,只需要每次改变传入的参数的顺序,即可实现功能。

程序实战

C语言程序设计——小学生口算刷题系统

参考资料

《C语言程序设计(第3版)》——何钦铭、颜辉

posted @ 2019-11-03 15:15  乌漆WhiteMoon  阅读(1146)  评论(3编辑  收藏  举报