6.7 使用函数小结
6.7.1 使用函数的步骤和方法
以下使用函数的步骤和方法,有些是C语言本身的要求,有些是常规作法,而有些是本书的建议。
(1)使用任何函数(无论是自己定义的函数还是已经编译好了的库函数)之前,首先进行函数声明即写出函数原型,对于自定义函数这意味着首先为函数命名(建议)。
(2)函数原型需要写出函数需要几个什么样的参量以及它们的次序来求一个什么样的量(C语言的要求)。
(3)对于库函数可以用编译预处理命令#include实现写函数原型的目的(常规,你也可以自己直接写出用到的函数的函数原型)。
(4)函数原型一般写在main()之前源程序一开始的地方(建议)。
(5)函数原型后有“;”(C语言的要求)。
(6)写函数定义时可以把写好的函数原型复制过来适当编辑(建议)。
(7)函数定义由函数头和函数体两部分组成。函数头后面没有“;”,而函数体以“{”、“}”为开始和结束标志(C语言的要求)。
(8)注意函数的返回值与函数的类型一致(良好的编程风格,否则可能会自找麻烦)。
(9)进行函数调用时,要注意实参与形参在类型与个数上应该一致(C语言的要求)。
(10)不要写与计算次序有关的函数调用(容易被忽视的错误写法)。
6.7.2 常见问题
以下列举了初学者使用函数时常见的一些错误:
这段代码的错误在于把h()函数的定义写在了g()函数定义的内部,这种情况叫做嵌套定义。C语言不允许函数嵌套定义。C语言中各个函数的地位是平等的,各函数定义的位置是平行的,每个函数的定义都必须写在其他函数定义的外部。
这个函数定义的问题在于函数的类型是int,但是在函数体内计算完成之后却没有return语句把计算出的值返回到函数调用处。
这段代码的错误同样是忘记return语句——只写了n<m成立时函数的返回值,但忽视了在n<m不成立时函数的返回值。
这段代码犯了重复定义变量的错误,形参x、y与函数的局部变量x、y重名且处在相同的代码区域,编译器无法区分后面使用的x、y究竟是哪个x、y。这样的代码无法通过编译。
函数头与函数体之间没有“;”,这是在语法格式方面的错误。此外要注意的是,在函数原型后面是有“;”的。
这段代码的错误在于混淆了形参的说明格式与变量的定义格式。变量的定义:
表示x和y都是double类型的变量,但是对于形参则必须逐个说明,前面的函数定义应该写成:
形参写成double x, y,在老式的C语言中表示的是x为double类型,y为int类型(默认的类型被编译器认为是int类型),在C99中已经不再允许了。由此可以看出,这是一种被淘汰的写法,即使目前合法也不应该使用。
这段代码没有语法错误,也能够顺利执行。但要特别注意的是,函数返回值的类型与函数类型不一致。如果不是为了某种特别需要,且是在清楚地了解程序确切行为的前提下特意这样写,那么这段代码的行为很可能是有时表现得正确有时表现得不正确。这种错误在程序调试过程中很难查找,归根结底是由忽视数据类型及类型检查这样的不良编程习惯造成的。
此外在函数调用时,初学者容易犯实参与形参不对应的错误:有时是个数不对应,有时是类型不一致,而有时是顺序不一致。如果有写函数原型的好习惯,这类错误很容易幸运地被编译器检查出来。如果没有写函数原型的习惯,这类问题很可能一直潜伏在程序内部而难以被察觉,即使察觉了也很难定位错误并纠正。
有一种编程风格喜欢用函数定义来代替函数声明。具体的做法是,把所有的函数定义都写在函数调用的前面,如下面代码所示:
对于编译器来说,这和写了函数声明的效果是一样的。然而姑且不论把所有的函数定义都写在函数调用的前面是否可行(有些情况下不借助extern这个关键字是办不到的),即使可行,这样的代码结构势必导致main()函数被写在最后。然而main()函数是程序最主要的思路之所在,要阅读这样一个程序就必须要从后往前看。换句话说,这种风格对代码的阅读者之一——编译器,没有任何妨碍,但却给代码的另一个阅读者——人,造成了许多阅读障碍。当然,如果有人写文章时的确是喜欢把标题和概述写在文章末尾的话,那么对把main()放到源代码最后的这种风格也就没什么好评论的了。
练习
已知2008年1月1日是星期二,且2008年是闰年。编程:输入2008年任意一天的日期,输出这天是星期几。要求判断输入的日期是否合理等功能用函数实现。下面是一个建议性的程序框架,函数定义部分请读者自行完成。