在C语言中,函数可以说是陪伴着我们成长的,那么我们今天来看看什么函数
1.函数是什么
函数在我们在数学中已经用到,但是在编写代码中又赋予了一些不同的意义,那就是程序的子程序,那么这个子程序我们怎么去理解呢,用一个形象的比喻其实就是,把整个程序比作一个汽车,函数就是这个汽车中的零件,由很多的函数一起构成了整个程序。
而函数又分为了库函数和自定义函数
2.库函数
库函数是什么呢,库函数是谁写的呢?让我们带着疑问看看。
库函数其实就是一些封装好的函数,给使用者提供了极大的便利,让我们可以直接使用现成的函数就可以实现一些功能,不需要我们自己再去实现这个函数。
库函数很多人以为是C语言语法自己带着的,其实并不是,他是众多编译器厂商一起讨论实现定义的 ,虽然他们里面实现的内容不太一样,但是最后想要得到的结果和大体相同的
让我们来看几个库函数:
#include <math.h>
void Test()
{
int n = (int)pow(2, 5);
printf("%d ", n);
}
通过我们去查找文档仔细阅读后,看到了两个参数,一个是double 的底数 ,一个数double的次方数,返回值为double,知道了这些,我们去看上方代码,我们得到的N就是我们要的结果,通过调用库函数,就可以直接使用,极大便利了我们,当然了,在调用库函数的时候一定要调用头文件。
3.自定义函数
有人说,有了库函数就可以了啊,为什么要自定义函数。这我就不得不说了,如果库函数把所以的事情都干了,那么要程序员干什么呢,有了自定义函数,才让我们有了很大的发挥空间。
让我们看看自定义函数
ret_type fun_name(type paral)
{
......内容
}
这就是自定义函数的形式 ret_type 是函数的返回类型 fun_name 函数名 type paral 函数参数
{}里面写函数内容。
MAX函数示例:
int Max(int x, int y)
{
return x > y ? x : y;
}
void Test2()
{
int a = 10;
int b = 20;
int ret = Max(a,b);
printf("%d ", ret);
}
我实现了一个简单的自定义函数Max,int 就是函数返回值的类型为int .Max是函数名字,int x,int y是函数的参数(形参)。
4.函数的参数
实参:常量,变量,表达式,函数
让我们看上面的MAX函数示例,Test函数中,Max(a,b)中a,b就是实际参数
形参:用来接收实参,需要写出类型和接收变量。需要与实参匹配,比如实参是int 那么形参也需要是int 类型匹配,如果实参传的是地址,那么形参就必须是指针。
当然,形参是实参的一份零时拷贝,如果没有调用,那么形参并不会开辟空间。
并且形参的生命是随着这个函数生命一起结束销毁的。
让我们看上面的MAX函数示例,Max函数中,x,y就是形参。
5.函数的调用
函数的调用分为传值调用和传址调用。让我们来看两个函数来理解
void Swap(int x,int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
void Test3()
{
int a = 10;
int b = 20;
Swap(a, b);
printf("%d %d",a,b);
}
上方的Swap我想要实现的是交换两个值 ,但是我们发现,x,y 的值交换了,但是a,b的值并没有交换,这是因为我们用的是传值调用,只是单单把a,b的数值拷贝了一份传给了x,y。在Swap中交换了x,y后,并不会影响原值a,b。通俗的说,就是复印件的改变并不会影响原件
void Swap(int* x,int* y)
{
int tmp = 0;
tmp = *x;
*x = *y;
*y = tmp;
}
void Test4()
{
int a = 10;
int b = 20;
Swap(&a, &b);
printf("%d %d",a,b);
}
那么再看修改后的Swap函数,这个函数打印后我们发现,a.b的值发生了交换,这是为什么呢。
因为我们看到实参是a,b的地址,上面形参用指针接收,通过把a,b的地址传过去,解引用地址去修改原值。这也就是传址调用。
6.函数的嵌套调用和链接访问
我们看上面的Swap函数示例,可以说就是一个函数的嵌套调用,在Test4中嵌套着Swap函数,
当然了,函数可以嵌套调用,但是不可以嵌套着定义。
而关于链式访问,让我们来看一段代码
void Test5()
{
printf("%d", printf("%d", 43));
}
这个就是一段链式访问,把一个函数的返回值作为另一个函数的参数。
7.函数的声明与定义
在我们要完成一个程序,我们不会吧所以代码都写在一个.c文件中,因为这样写一是比较混乱,比较难修改,二是可读性也比较低,所以在项目中我们一般都是声明与定义分离的写法,把声明写到.h文件中,把定义写到.c文件中,这样写后我们就比较好维护,可读性也比较高。
在写函数时,我们都会把函数定义写在test函数上面,这样才能向上查找,如果把函数的定义写道test函数的后面,向上查找就会找不到,当然,我们只要把函数的声明放到test函数上面也可以。
我们可以看到 我们有两个.cpp文件 ,一个测试,一个定义,还有一个.h头文件用来声明
当然,在使用的时候.cpp,都要包含.h头文件。(在.h中我们可以把库函数的头文件都定义进去,.cpp在使用的时候,直接展开.h就可以把所有库函数头文件展开)
8.函数的递归
函数的递归中心思想就是把一个大的问题,一层一层转化为一个小的问题去解决。
也就是 “大事化小”
当然,在执行递归的时候,也是需要必要条件的,如果不满足条件,可能就会发生不可预期的事情。
条件:
1.递归存在限制条件,满足了限制条件,递归就会停止
2.每一次递归都会接近这个限制条件
让我们看一组代码示例
int Test(int n)
{
if (n == 1)
{
return 1;
}
return n * Test(n - 1);
}
int main()
{
//求N的阶乘 ,思路:N的阶乘就是N-1的阶乘 乘以 N
int n = 5;
printf("%d ",Test(n));
}
求N的阶乘,思路就是把求N的阶乘转化为求(N-1)*N的阶乘把大问题转化为小问题,当N=1的时候递归不再继续。
当然了,这次的递归只是初次讲解,作为一个重点,会专门开一个章节解析递归。