函数概念
在C语言中,函数通常是为了完成某一项特定任务的一小段代码,所以又可以把函数叫做子程序。C语言的程序其实是由无数个小函数组合成的,特别是在复杂的情境下,复杂的任务可以拆解为一个个简单的任务由子程序完成。同时如果一个函数可以解决某一个特定任务的话,这个函数可以复用,提高了软件开发的效率。
在C语言中,我们一般接触到的两种函数:库函数,自定义函数。
库函数
C语言标准中规定了C语言中的各种语法和规则,它并不提供库函数,C语言的国际标准ANSI C规定了一些常用函数的标准,称为标准库。编译器产商就根据这个标准给出了一系列函数,这些函数就是库函数。有了库函数后,一些常见的功能就不需要程序员自己实现了,一定的提升了执行效率。各种编译器的标准库中提供了一系列的库函数,这些库函数根据功能的划分,都在不同的头文件进行了声明。
这里是库函数相关头文件查询网址
如果想快速了解某一个函数的功能与如何使用,可以询问ChatGPT来方便查询资料的过程
注意:在使用库函数之前,必须先引用对应的头文件进行声明,不然会报错
自定义函数
相比于库函数,我们应该把目光聚焦在自定义函数上,原因是它能赋予程序员写代码更多的创造性
下面是一个自定义函数的举例
#include <stdio.h>
int ADD(int x, int y) //int是函数的返回类型,括号内是接收的参数
{
int c = 0;
c = x + y;
return c; //返回值会回到主函数
}
int main()
{
int a = 0; int b = 0;
scanf("%d %d", &a, &b);
int sum = ADD(a, b); //这里的括号是调用ADD函数并且将参数传给ADD函数,返回值赋给sum
printf("%d", sum);
return 0;
}
ADD前面的int代表着函数的返回数据类型,如果不返回数据就是void,从函数返回的数据要存放在一个变量里
形参和实参
实参(实际参数)就是真实传给自定义函数的参数,也就是a和b。而形参(形式参数)就是在调用函数时,程序向内存临时申请空间存放数据的参数,也就是x和y。它们之间的关系可以理解为形参是实参的一份临时拷贝。
return语句
return是返回的意思,用来结束函数的执行。
下面是return语句使用的注意事项:
1.return后面可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回结果
2.return后面可以什么都没有,适用于返回类型为void的函数
3.如果函数中存在if等分支的语句,则要保证每种情况下都有return返回,不然会出现错误
4.return返回的值必须要和返回类型相同
嵌套调用和链式访问
嵌套调用
所谓嵌套调用就是在一个函数中调用另外一个函数,例如我们在main函数中调用了各种各样的库函数,或者我们在一个子程序中调用另外一个子程序。
#include <stdio.h>
int square(int num)
{
return num * num;
}
int cube(int num)
{
int squareNum = square(num);
return squareNum * num;
}
int main()
{
int num;
printf("请输入一个整数:");
scanf("%d", &num);
int NUM1 = square(num);
printf("%d 的平方:%d\n", num, NUM1);
int NUM2 = cube(num);
printf("%d 的立方:%d\n", num, NUM2);
return 0;
}
square和cube是两个嵌套函数。square函数计算一个数的平方,而cube函数则调用square函数来计算一个数的平方,并返回平方结果与原数相乘的结果,即为该数的立方。
通过嵌套函数的调用,我们可以将计算复杂的操作进行模块化和重用,提高代码的可读性和可维护性。
链式访问
链式访问就是将一个函数的返回值作为另外一个函数的参数,像链条一样将函数串起来。自右向左执行。
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
来看这个例子,我们知道printf函数返回的是打印的字符个数,首先第三个printf会在屏幕上打印43这个数字,然后返回2,第二个printf打印2并且返回1,第一个printf就打印1。综上,程序输出的结果是“4321”被打印在屏幕上。
函数的声明和定义
#include <stdio.h>
/*
int ADD(int x, int y)
{
int c = 0;
c = x + y; 这里是函数的定义
return c;
}
*/
int main()
{
int a = 0; int b = 0;
scanf("%d %d", &a, &b);
int sum = ADD(a, b); //这里是函数的调用
printf("%d", sum);
return 0;
}
还是这个例子,被注释掉的一段就是函数的定义,我们把函数放在主函数前面是不需要声明的,但是如果ADD函数这段代码放在了主函数后面,程序就会出现警告信息,这就是没有声明的缘故,函数声明中参数只保留类型,只需要交代清楚:函数名,函数返回类型,函数参数
#include <stdio.h>
int ADD(int x,int y); //这里是函数的声明
int main()
{
int a = 0; int b = 0;
scanf("%d %d", &a, &b);
int sum = ADD(a, b);
printf("%d", sum);
return 0;
}
int ADD(int x, int y)
{
int c = 0;
c = x + y;
return c;
}
多个文件
一般情况下,函数的声明,类型的声明放在头文件(.h)中,函数的实现是放在源文件(.c)中
如图所示,这个经常用于多人合作写代码项目中。
函数的递归
函数的递归简单来说就是自己调用自己,递就是传递,归就是回归。
递归的限制条件
递归在书写过程中有两个必要条件
- 递归存在限制条件,当满足这个限制条件时,递归便不再继续
- 每次递归调用之后应越来越接近这个限制条件
递归举例
例1:求n的阶乘
#include <stdio.h>
int Fact(int n)
{
if(n<=0)
return 1;
else
return n*Fact(n-1); //n!=1*2*3*.....*(n-1)*n
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fact(n);
printf("%d\n", ret);
return 0;
}
例2:求n的k次方
#include <stdio.h>
int fun(int n,int k)
{
if(k<1)
return 1;
else
return n*fun(n,k-1)
}
int main()
{
int n = 0;int k = 0;
scanf("%d %d",&n,&k);
int sq = fun(n,k);
printf("%d",sq);
return 0;
}
注意事项
在复杂的,需要重复调用函数的情况下尽量不去使用递归,因为每一次函数调用都会向内存申请一块空间来存储变量,这块空间被称为运行时堆栈,或者函数栈帧。函数不返回,函数栈帧就一直占用,一旦申请的内存过多就会出现栈溢出的问题。所以在这种情况下可以用迭代(循环)的方式进行优化,