4.1函数的定义到目前为止,我们所有的程序都是以“main()”函数作为程序的唯一入口的,对main函数的解释,也就仅限于其作为整个程序的入口。但是函数的定义,入口参数,返回值等具体细节我们到目前为止还没有进行一个仔细的探讨,本章节开始,我们会对函数做一个全面深入的理解。函数,这个名词我们最早接触到的是在中学的数学里面,它可以用一个方程式来表示,比如,x,y之间存在某种关系,我们就可以表示成y=f(x)的形式。而C语言中的函数是用来解决某一个具体问题而编写的一个或者某个程序块。举个例子来说,如果我们要写一个简单的程序,比较输入的两个数a,b的大小,那么可以写成如图4-1-1所示的程序。图4-1-1中,就是对输入的两个数值进行比较,我们将其整个比较过程全部都写在了main函数里面,然后整个main函数就能完成比较大小的功能了。但是现在问题来了,假设我下面又要比较另外两个数的大小c和d,那么我是不是需要再重新把整个比较数值大小的流程代码全部再写一遍?这样子写出来的重复代码太过繁杂,因此大多数编程语言为了解决这个问题,都不约而同地发明出了一个东西,叫做函数。不仅高级语言有函数,就连汇编语言,也都是支持功能模块的形式代码,只不过汇编语言里面的函数一般被称为子程序。图4-1-1 两个数值比较大小函数的好处,除了上面所述的减少代码的繁琐度,或者提高代码的重复率之外,还有一个好处可能做应用软件的程序员体会不到。这个是只有下位机软件工程师才能体会到的好处。我们现在PC机端的C语言打印字符,只用了printf函数,这是C语言的库函数,因此当我们用MinGW编译完成之后,没问题,将代码完整地复制到Linux底下,再用GCC编译一遍也可以正常使用,这个是没有任何问题的。有问题的地方是,假设使用一个我们在PC上面编写好的一个C语言程序,需要一直到单片机上面去运行,也许这个程序非常简单,也就是只有一行打印字符的函数,如果没有函数存在,那我们要把整个打印的机制全部写下来,包括液晶扫描时序,但是C语言提供了printf库函数给我们,因此在移植的时候,只需要修改printf函数里面的putchar函数,对其输出重定向到单片机液晶字符打印程序即可,这个也就是所谓的可以执性。有些书只会告诉你,C语言由于可以编译生成不同平台的机器代码,所以它的移植性好,这个我觉得是片面的,真正C语言移植性比汇编语言好的地方,正是由于这些自由度,效率都很高的函数存在,才提高了C语言代码的可移植性。好了,上面花了大片篇幅解释了C 语言函数的好处,接下来我们就来说下C语言如何去定义一个函数。返回类型,又叫函数类型,指的是,这个函数程序执行完成之后,返回给调用者的数值类型,函数的返回类型支持我们之前学习的一些基本数据类型,以及后需要学习的如指针类型等高级类型。如果函数不需要返回,那么我们也可以将返回类型设为空类型,即用“void”表示。但是当函数一旦定义成非空类型函数时,一定要在函数体里面用“return”关键词返回。函数名就是指这个函数的名字,函数名以字母,下划线开头,但是后续字母可以包含数字,下划线,字母。后面的形式参数,就是你需要再函数调用时传入的参数,比如定义了一个函数叫做void CompareNumber(int a, int b),那么这个a和b就是需要传入的形式参数,我们可以再主函数里面将a,b输入,然后在调用时将这两个变量传入即可。这就好比是数学里面的函数,一个两个形式参数的函数,就好比是一个二元函数,假设有个函数f(x,y)=2x+y,那么这里的x,y就相当是两个形式参数,传入后,这里的f(x,y)就相当于是其返回值。有些书上写,函数在使用时一定要定义在另一个调用它的函数之前,这个是很正确的,但是一般我们还是推荐函数要遵循先声明,后定义的方式,且主函数一定要写在最前面,关于函数的声明,这里牵扯到非常多的细节,有类似于高级语言里面所谓的“私有函数”,“公有函数等”,我们后面会单独开辟一小节来详细讨论。总之从这一节往后,我们尽可能地将功能代码用函数封装起来。还是以之前比较大小的程序为例,现在我们将整个比较大小的过程用函数封装起来,给main函数调用,一开始,我们先从最简单的无返回值函数写起。如图4-1-2所示。图4-1-2 比较大小函数图中,include小面一行,即为函数的声明,函数的声明很简单,就是把整个函数定义第一行完全写一遍,在最后加上英文下的分号,起作用就是告诉编译器在编译的时候,我这里有这个函数,不是未定义的。具体这个函数的定义,就是下面函数体里的内容。