函数是完成特定任务的独立程序代码单元(也就是完成某项特定的任务的一小段代码),有些翻译为子程序,C语言的程序其实是由无数个小的函数组合而成的,也可以说,一个 大的计算任务可以分解成若干个较小的函数完成
1、首先,使用函数可以省去编写重复代码的苦差,如果程勋要多次完成某项任务,那么只需要编写一个合适的函数,就可以在需要的时候使用这个函数
2、其次,即使程序只完成某项任务一次,也值得使用函数,因为函数让程序更加模块化,从而提高程序代码的可读性,更方便后期修改,完善。
许多程序员喜欢把函数看作是根据传入信息(输入)及其生成的值或者响应的动作(输出)来定义的“黑盒”(相当于把函数的代码包起来的盒子)。如果不是自己编写函数,根本不用担心“黑盒”内部行为。例如:使用printf()时,只需要知道给这个函数传入格式化字符串或一些参数以及printf()生成的输出,无需了解printf()的内部代码。以这种方式看待函数有助于把注意力集中在程序的整体设计上,而不是函数的实现细节上,因此,在动手编写代码之前,仔细考虑一下函数应该完成什么任务,以及函数和程序整体的关系。
在C语言中我们一般会见到两类函数:
1、库函数
2、自定义函数
初学者常使用的printf,scanf都是库函数,库函数也是函数,不过这些函数已经是现成的,我们只要学会就能直接使用了。有了库函数,一些常见的功能就不需要程序员自己实现了,一定程度提高了开发效率,同时库函数的质量和执行效率更有保证。
库函数有很多,有数学相关的,有字符串相关的,有日期相关的等等,每一个头文件都包含了相关的函数和类型等信息,库函数的学习不用着急一次性全部学会,慢慢学习,各个击破就行
举例:sqrt
sqrt表示compute square root 计算平方根
头文件包含:
库函数是标准库中对应的头文件中声明的,所以库函数的使用,务必包含对应的头文件,不包含可能会出现一些问题。
其实自定义函数和库函数是一样的,形式如下:
ret_type fun_name(形式参数)
{
}
这里ret_type 是函数返回类型,fun_name 是函数名,括号中放的是形式参数,{}括起来的是函数体
具体如下:
注意:1、函数名是自定义的,根据实际情况起名字
2、参数的个数也是根据实际情况来确定,可以有0个参数,也可以有多个参数
3、函数的返回值:函数可以返回值,也可以不返回,要根据实际的情况来写,函数不返回值的时候,返回类型写void
其实我们可以把函数想象成小型的一个加工厂,工厂得输入原材料,经过工厂加工才能生产出产品,那函数也是一样的,函数一般会输入一些值(可以是0个,也可以是多个),经过函数内的计算,得出结果。
ret_type 是用来表示函数计算结果的类型,有时候返回类型可以是void,表示什么都不返回
fun_name 是为了方便使用函数,就像人的名字一样,有了名字方便称呼,函数有了名字方便调用,所以函数名尽量要根据函数的功能起的有意义。
函数的参数相当于,工厂中送进去的原材料,函数的参数也可以是void,明确表示函数没有参数。如果有参数,要交代清楚参数的类型和名字,以及参数个数。
{}括起来的部分称为函数体,函数体就是完成计算的过程。
下面编写一个加法的函数:
其中Add函数也可以简写为:
在函数的使用过程中,我们把函数的参数分为实参和形参:
比如这里的第一行,改行告知编译器Add()使用两个参数x和y ,这两个参数都是int类型。这两个变量被称为形式参数,简称形参。
为什么叫形式参数呢?实际上,如果只是定义了Add函数,而不去调用的话,Add函数的参数x和y 只是形式上存在的,不会向内存申请空间,不会真实存在的,所以叫形式参数。形式参数只有在函数调用的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形式的实际化。
和定义在函数中的变量一样,形式参数也是局部变量,属该函数私有,这意味着在其他函数中使用同名变量不会引起名称冲突,每次调用函数,就会给这些变量赋值。
特别注意的是:ANSI C 要求在每个变量前都声明其类型。也就是说,不能像普通变量声明那样使用同一类型的变量列表(有点抽象,举个例子):
比如我们输入10和20之后,这两个值分别存放在x和y之中,当执行到Add函数时,这两个值被赋值给Add()中相应的形式参数:变量x和y,简而言之,形式参数是被调函数中的变量,实际参数是主调函数赋给被调函数的具体值。实际参数可以是常量、变量,或甚至是更复杂的表达式。无论实际参数是何种形式都要被求值,然后该值被拷贝给被调函数相应的形式参数。
在函数的设计中,函数中经常会出现return语句,这里讲一下return语句使用的注意事项。
1、return后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。
2、return后边也可以什么都没有,直接写return,这种写法适合返回函数类型是void的情况。
3、return返回的值和函数返回类型不一样,系统会自动将返回的值隐式转换为函数的返回类型。
4、return语句执行后,函数就彻底返回,后边的代码不再执行。
5、如果函数中存在if等分支的语句,则要保证每种情况下都有return返回,否则会出现编译报错。
在使用函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进行操作。
比如:写一个函数对将一个整型数组的内容,全部置为-1,再写一个函数打印数组的内容。
简单考虑一下,基本的形式应该是这样的:
这里的set_arr函数要能对数组内容进行设置,就得把数组作为函数传递给函数,同时函数内部在设置数组每个元素时,也得遍历数组,需要知道数组的元素个数。所以我们需要给set_arr传递2个参数,一个是数组,另外一个是数组的元素个数。仔细分析print_arr也是一样的,只有拿到了数组和元素个数,才能遍历打印数组的每个元素。
数组作为参数传递了set_arr和print_arr函数了,那这两个函数应该如何设计呢?
这里我们需要知道数组传参的几个重要知识:
1、函数的形式参数要和函数的实参个数匹配
2、函数的实参是数组,形参也是可以写成数组形式的
3、形参如果是一维数组,数组大小可以省略不写
4、形参如果是二维数组,行可以省略,但是列不能省略
5、数组传参,形参是不会创建新的数组的
6、形参操作的数组和实参的数组是同一个数(因为数组传的是地址,数组地址不变)
嵌套调用就是函数之间互相调用,每个函数就行一个乐高零件,正是因为多个乐高的零件互相无缝的配合才能搭建出精美的乐高玩具,也正是因为函数之间有效的互相调用,最后写出来了相对大型的程序。
所谓链式访问就是将一个函数的返回值作为另外一个函数的参数,像链条一样将函数串起来就是函数的链式访问。
比如:
上面的代码完成动作写了两条语句,如果把strlen的返回值直接作为printf函数的参数呢?这样就是一个链式访问的例子了。
下面来看一个有趣的代码,这个执行的结果是什么呢
printf函数返回的是打印在屏幕上的字符个数。
上面的例子中,我们就第一个printf打印的是第二个printf的返回值,第二个printf打印的是第三个printf的返回值。
第三个printf打印43,在屏幕上打印2个字符,再返回2
第二个printf打印2,在屏幕上打印1个字符,再返回1
第一个printf打印1
所以屏幕上最终打印:4321
今天的笔记到此结束,谢谢各位观看