我们之前编写的程序多数只有一个 main函数,其实,在一些大型的程序通常由多个函数组成,main函数里就是一些调用其他函数的语句。
1 C语言程序结构
C程序是面向过程的程序设计方式,此外有面向对象的程序设计方式(如C++程序、Java程序、Python程序等)。
面向过程的程序设计方式是按步骤来一步一步执行下去,那么面向对象的程序设计方式是怎样的呢?
面向对象的程序设计方式不是先看有什么步骤,而是先看有哪些类,在类的实现里再敲定有哪些步骤。
所以无论是面向过程还是面向对象,都还是要有步骤的。
C语言的程序单位是函数,一个C程序由多个函数组成,每个函数完成一定的功能,当需要实现什么功能时,就调用那个相应的函数。
C程序是把大问题分解成若干小问题,小问题再进一步分解成若干更小的问题,每个小问题用一个函数实现。
写程序时,用main()解决整个问题,它调用解决小问题的函数,必要时这些函数又进一步调用解决更小问题的函数,从而形成函数的嵌套调用。
这就是C语言的程序结构,如图所示:
所以C语言中函数是一个很重要的知识点。
我们来看一个程序,这个程序省略了很多代码,因为有些东西没学到,用这个程序作为例子只是为了给大家看C语言的程序结构。
程序1:
在主函数里,调用了6个用户自定义函数,程序1只写了主函数和display_menu两个函数的代码,其他函数的代码省略了。
程序1的switch语句块里面有一个default语句,default的意思是当输入不是1-6时,执行default,显示“无效选择,请重试”。
由于其他函数的代码都没有写,所以输入1-5时,没有操作,只是重新出现菜单,输入6时退出循环,程序结束。
程序1运行结果如下:
2 指针作为函数参数的作用
程序1的函数定义时都没有参数,当定义一个函数有参数时,这个参数叫形式参数(形参),调用该函数时的参数叫实际参数(实参)。
形参在函数里的变化无法影响对应的实参。
也就是说,如果形参变化了,我想把结果传回给主调函数时,是没法传回来的。
程序2:
主函数调用函数f1,实参为x,值为9;在函数f1中,讲形参a的值加上100,那么a的值变成109,但是调用结束回到主函数以后,x的值是9还是109呢?x的值还是9。我们来看运行结果:
但是当我们把程序改一下,形参a不是int类型,而是指针类型int *,调用时传过去的实参是地址,我们来看这个程序:
程序3:
程序3,f1的形参a是指针类型,调用f1时的实参就必须是地址&x,将x的地址传给了a,然后在函数f1里面,我们看到这么一句:
*a = *a + 100 ;
*a是a这个地址里面的值,原来的值加了100,变成了109。
a地址里面的值就是x啊,所以函数f1运行结束返回主函数时,x的值变了,不再是9,而是109。
程序3运行结果:
这个运行结果的第2行的提示词实际是不对的,不是a=109,应该是*a=109,但是提示词是我们自己写printf的双引号里面的,你写的是什么它就原样输出。
这个例子告诉大家,当你想把在函数里面改变的结果返回给主调函数时,可以将函数的形参定义成指针类型,调用时的实参是地址。
可能有人会问了:不是说形参的改变不会影响实参吗?现在实参怎么变了呢?注意,变的不是a,变的是*a。函数里不是让a等于109,而是让*a等于109。
也就是说,在函数里,通过地址a,找到了地址a里的内容*a,把*a的值改变了。而*a就是x,因为a就是x的房号(&x),所以*a就是x。
再看一个例子:
程序4:
程序4中函数swap的功能是交换两个形参的值,但是调用结束返回主函数时,x和y的值并没有交换。因为形参的值不影响实参。
程序4运行结果:
但是如果将程序4的函数swap的形参类型改为指针型,函数体里的a改成*a, b改为*b,调用时实参地址传给形参,那么调用结束返回主函数时x和y的值就会改变。
程序5:
调用函数swap时实参是地址,将地址传给形参(指针),地址并没有被形参改变,但是改变的是地址里面的内容,*a和*b变了,注意a和b没有变,变得是*a和*b。
程序5运行结果:
3 总结
如果定义函数时它的形式参数是指针类型的变量,则调用该函数时的实际参数必须是地址,此时函数里面改变的结果可以保存下来回传给主调函数。