Bootstrap

【C语言笔记】函数详解


函数是C语言程序的基本单位,函数在C语言程序中发挥着重要的作用,可以说C语言离不开函数。

一、函数的定义

在维基百科中将函数定义为子程序

——在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组
成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
——一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软
件库。
----------引自维基百科

二、函数的分类

C语言中函数分成两类:库函数和自定义函数

1、库函数

我们在编写程序时,总会频繁地用到一些逻辑非常相似或者功能相同的代码,比如要将信息按照一定格式打印到屏幕上,或者字符串的赋值(拷贝),或者接收用户输入的信息。
倘若我们每次都把相同的代码都写一遍,就会产生大量的代码冗余,不利于阅读。
库函数就为我们解决了这个问题,它将上面所用到的基本功能都封装成函数,需要用到的时候直接调用其函数就行了,大大减少了代码的冗余,调高了代码的独立性。

库函数的学习

学习库函数我们并不需要去记去背,C语言中有成千上万的库函数,若全都背下来,那就太不人道了。
其实库函数虽多,但我们平时使用的时候,只需要掌握其中常用的就行了,对于一些不常用的我们在遇到的时候,可以去查文档。
这里推荐一些很好的网站应用程序,可以帮助我们学习库函数
(1)www.cplusplus.com
(2)https://en.cppreference.com/w/
(3)msdn
虽然我们有了这些很强力的工具,但是还是有一个问题摆在我们面前,就是这些工具的文档都是英文的,虽然我们的浏览器能够翻译,但那些翻译还是太蹩脚,有点不准确,所以我们还是要去做那件让大多数中国学生头疼的事——学好英语!!!

2、自定义函数

我们需要思考这样一个问题:如果库函数能够解决所有问题,那还要程序员干嘛?
当然,C语言中的库函数已经非常强大了,但现实世界的情况是千变万化的,我们不可囊用有限的库函数来解决现实中无限的问题,所以对于我们程序员来说,更重要的是要学习怎么定义自定义函数。
其实自定义函数和库函数本质上是一样的,都是人定义的,只不过库函数是开发C语言的程序员定义的,我们不可以修改,但自定义函数是我们自己定义的,我们可以随便修改。

自定义函数的组成

自定义函数的主要组成有函数名、返回类型、形式参数。
基本结构:

ret_type fun_name(para1, * )
{
    statement;//语句项
}
/*
ret_type 返回类型
fun_name 函数名
para1    函数参数
*/
示例

写一个函数,求两个数的和:
在这里插入图片描述

写一个函数,求出两个数的较大值:
在这里插入图片描述

三、函数的参数

1、实际参数

实际参数指的是在调用函数时放在函数调用操作符(圆括号)中的变量,例如调用printf函数时:printf(“%d”, a);中的a变量就是一个实际参数。
实际参数可以是:常量、变量、表达式、函数等。
在实际参数是函数时,当做实际参数的那个函数一定要有确定的返回值。

2、形式参数

形式参数指的是,函数在定义的时候阔汉中的变量
形式参数只有调用函数的时候才创建(实例化)在函数调用结束后就会被回收,故形式参数只在函数内部有效,且形式参数是实例化后相当于实际参数的一份临时拷贝。

四、函数的调用

1、传值调用

传值调用的时候,实际参数和形式参数分别占用不同的内存单元(地址不同),故形式参数和实际参数之间没有任何联系。

2、传址调用

传址调用是把函数外部定义的变量的地址传给函数,这种传参方式就可以做到在函数内部对函数外部的变量进行修改,因为通过变量的地址就可以找到对应的变量。

3、传值调用和传址调用的区别

举一个例子来说明一下,函数传值调用和传址调用的区别
比如我们现在要写一个函数,我们希望调用这个函数后能将两个变量的值进行转换
错误示范:
在这里插入图片描述
通过结果我们可以看出两个变量的值并没交换,这是因为函数内部的变量与外部的变量没有任何关系,实际交换的只是函数内部的变量。
正确写法:
在这里插入图片描述
我们可以看到,当我们把参数改成地址(指针)的形式时,就交换成功了,这恰恰说明了传址调用可以在函数内部对函数外部的变量进行修改。

五、函数的定义与声明

1、函数的定义

函数的定义就是交代这个函数的具体实现步骤或功能,但要注意的是函数不能嵌套定义,就是在一个函数里面不能再定义另一个函数
错误示范:

int max(int a1, int b1) {
	return a1 > b1 ? a1 : b1;
	int min(int a2, int b2) {
		return a2 < b2 ? a2 : b2;
	}
}

正确定义:

int max(int a1, int b1) {
	return a1 > b1 ? a1 : b1;
}
int min(int a2, int b2) {
	return a2 < b2 ? a2 : b2;
}

1、函数的声明

——函数的声明一般放在头文件内。
——函数的声明主要目的在于告诉编译器有一个函数存在,它的函数名是什么、参数是什么、返回值类型是什么。
——这个函数具体存不存在,编译器不管。
——函数声明要放在函数时用之前,满足先声明后使用。

五、函数的递归

1、什么是递归

递归是程序直接或间接调用自身的编程技巧(recursion)
递归是一种方法,它通常可以把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来解决,递归只需要少量代码就可以完成,大量的重复多次计算,大大减少了代码量。
递归的主要思考方式在于:把大事化小

2、递归的两个必要条件

——递归结束的条件,当满足这个条件之后,递归就不再继续。
——每次递归调用结束越来越接近这个条件。

3、举个例子

例如我们现在要写一个函数,帮我们顺序打印一个整数的每一位
例如
输入:12345
打印:1 2 3 4 5
在这里插入图片描述
思路:
我们如果想要得到一个数字的每一位,我们需要先%10得到最后一位,后/10除去最后一位,因为/10最后一位为余数,可以继续向前查找,直到这个数字成为一个一位数停止程序(因为如果这里是个一位数,a/10的值就是0,我们并不想打印0的每一位),所以在这里我们定义一个函数print(),它可以按顺序打印每一个值。
这个函数实际上的调用顺序是:
print(12345) print(1234) print(123) print(12) print(1)
没一个函数实际上打印的都是最后一位,而且函数调用是后调用的函数先结束,所以打印的顺序就是1 2 3 4 5

;