目录
1.函数的概念
在数学中,我们已经接触到了函数的概念,比如一次函数 y = kx + b(k ≠ 0),其中 k ,b 都是常数。而在C语言中,也有函数的概念,它是一组执行特定任务的语句集合,这个集合也就是代码块。其实,在一个C语言程序中,其代码是由无数个小函数组合而成,通过学习C语言,我们可以更好地理解程序的结构化设计,形成代码复用和模块化思想,并且这种方式提高了代码的可读性、可维护性和运行效率。
2.函数的分类
在C语言中,函数可以分为两类:库函数和自定义函数。
2.1 库函数
C语言库函数就是指C语言预先编写好的、可直接调用的函数集合,通过#include指令引入对应的头文件(如<stdio.h>),比如之前接触到的printf 函数和 scanf 函数都在这个库函数里面。每个函数都有返回类型,函数名和函数参数,这些库函数根据不同的功能划分为不同的函数,如输入输出、内存管理、字符串处理、数学计算、日期时间等。
返回类型 函数名(参数列表)
{
// 实现代码
}
需要注意的是,库函数并不是由C语言提供,而是由编译器的厂商提供。
库函数的使用:
在C语言程序中,如果要使用库函数,则需要引用头文件。比如,想要打印函数 printf 时,就需要引用头文件 <stdio.h>;想要计算一个数的平方根,就需要引用头文件 <math.h>。对于初学者,难以记住那么多的函数以及使用方法,这时我们就需要借助网址来学习。
https://legacy.cplusplus.com/
如下图,当我们搜索一个函数时,便会看到对应的库函数,功能介绍、举例和输出等。
比如这里我们写一个代码求一个数的平方根并打印出来。
2.2 自定义函数
虽然已经有了很多的库函数,但它并不能帮助我们解决所有的问题。
这时,就需要程序员自己定义一个函数。这为程序员写代码时创造了很大的发挥空间。自定义函数和库函数一样,要有返回类型,函数名和函数参数。如果函数类型是void,则表示什么都不返回。
函数类型 函数名(函数参数)
{
//代码实现
}
比如,写一个代码,用函数实现当输入两个整数时,求这两个整数的积并打印出来。
get_Mul便是我们自定义的函数名,函数参数分别是x和y,两个数的乘积结果是一个数值,所有返回类型是int。在这里,自定义的函数还可以简化为:
int get_Mul(int x,int y)
{
return x*y;
}
当然,在以后写代码自定义函数时,我们可以根据需求来设计函数,它们的返回类型,函数名和函数参数的个数都是可以灵活变化的。
3.函数参数
函数参数是C语言中实现函数间数据传递的重要机制。可以分为实际参数和形式参数。
3.1 实际参数
什么是实际参数?实际参数就是真实传递的参数,比如在上面实现两个数的乘积程序代码中,get_Mul是自定义的函数,而传给这个函数的参数a和b就是实际参数,简称实参。
实参可以是常量、变量、表达式、函数等。
3.2 形式参数
什么是形式参数?形式参数(Formal Parameter),简称形参,是函数定义中声明的参数,用于接收调用函数时传递的实际值。比如在上面实现两个数的乘积程序代码中,自定义函数get_Mul后括号的x和y,就是形参。
为什么叫形式参数?在上面的代码中,只要当代码执行到自定义函数get_Mul,才会对其调用,也就是上,如果不对其进行调用,但是x和y不影响程序的运行,比如我们把这个稍微改一下:
可以发现,当我们是直接算a和b的乘积时,哪怕自定义了 get_Mul 函数,但是没有调用也不会影响程序的正常运行。因此,形式参数只是形式上存在,不会真实存在,也不会占用内存空间,只有当被调用时,为了存放实参传递的值,才会向内存申请空间,并且形式参数当调用结束之后就自动销毁,其生命周期仅在自定义函数中。
3.3 实参和形参的关系
实参和形参之间有什么关系?在这里,我们直接调试(快捷键:fn+f11)上面的代码进行监视(VS2022:调试→窗口→监视,这里监视窗口任选一个即可):
调试过程中,在监视窗口里,我们看到x和y分别得到了a和b的值,但是他们的地址(这里&叫做取地址操作符)不一样,相当于是把实参的值复制给形参,这里我们叫做传值调用。相应的还有传址调用,这在后面会学习到。
4.嵌套调用和链式访问
4.1 嵌套调用
函数的嵌套调用是指在一个函数体内调用另一个函数,而被调用的函数又可以继续调用其他函数,形成多层调用关系。即函数A调用函数B,函数B又调用函数C……
比如,我们想知道当输入某年某月有多少天时,如果要用函数实现时,那我们就可以设计两个函数来实现,那么代码及运行结果如下:
当然,需要注意的是,在之前的分支和循环学习中,我们说不能过渡嵌套,在这里函数也是一样的,否则会影响代码的可读性,并且,函数可以嵌套,但不能嵌套定义。
4.2 链式访问
链式访问是指通过连续调用多个函数,将前一个函数的返回值作为后一个函数的参数,形成"链条"式的调用方式。
观察下面的代码:
在这里,strlen是一个计算字符串长度的函数,以 '\0' 作为结束标志。这在后面的学习中会学习。
在上面的代码中,把strlen的返回值作为printf的参数,就形成了链式访问。
5.函数的声明和定义
5.1 单个文件
单个文件函数的声明和定义,顾名思义就是在一个单独的文件中声明和定义。其实单个文件的函数声明和定义,在上面所写的代码就已经实现。比如这里我们实现一个加法:
红色框部分就是我们的函数的声明和定义。
5.2 多个文件
什么情况下我们会用到多个函数的声明和定义呢?当我们有一个很大的程序需要几个人分工或我们在写一个很多行代码时为了方便快速找到函数对其进行修改时,我们就需要用到多个文件。那么,多个文件的函数的声明和定义怎么实现呢?
像这样,我们把函数的声明放在Add.h文件,函数的定义放在Add.c 中,在test.c中书写我们的代码,也是可以得到一样的结果。
在这里,需要注意的是:
在Add.h头文件中,我们在声明一个函数时,最后要加一个分号,并且在引用这个自定义的头文件时,我们要使用双引号而不是尖括号,如#include "Add.h"。
6.函数递归
函数递归是什么?这里先做一个简单的概念理解,递归就是函数自己调用自己,其思考方式是把大事化小的过程。这个过程对于初学者(比如笔者)会比较困难,所以笔者在这里先卖一个关子,下期做一个函数递归的详解。