最近回过头又学习了下函数的内容,发现自己对于函数其实还有相当一部分没有深入了解的,于是写下这篇博客用以加强记忆,顺便与大家共勉。
首先函数分为库函数以及自定义函数,库函数是编译器自行提供的函数,只需要在.c文件中包含对应的头文件就能够使用,最常用的比如stdio.h中的printf函数以及scanf函数等等,但是如果只是有库函数,那么程序员在编写代码的时候必然会有不便的时候,这个时候就轮到自定义函数出场了。
自定义函数顾名思义,就是程序员根据需要来自己定义的函数,比如,假如我需要求多个球的体积,虽然这个代码很好实现,但是没计算依次就需要敲一次计算的代码未免过于繁琐了,于是我们可以自定义一个函数,这样我们需要计算的时候直接引用就行了,不仅缩短了代码,也使得代码可读性变强了。
#include<stdio.h>
#define N 3.14
float fun(int n)
{
return 4/3.0*N*n*n*n;
}
int main()
{
int n1,n2,n3;
printf("请输入第一个球的半径:");
scanf("%d",&n1);
printf("该球的体积是:%f\n",fun(n1));
printf("请输入第二个球的半径:");
scanf("%d",&n2);
printf("该球的体积是:%f\n",fun(n2));
printf("请输入第三个球的半径:");
scanf("%d",&n3);
printf("该球的体积是:%f",fun(n3));
return 0;
}
上面那条代码中,我自定义了一个名叫fun的函数,将输入的实参n1,n2,n3分别引用到函数里面,并且返回了一个float类型的数据用来输出。
一个自定义函数有以下几个要素:
1. 函数名;
我上面定义的函数中,fun就是它的函数名。
2.参数;
在上面的代码中,n1,n2,n3是fun函数的实参,而n是fun函数的形参;
3.返回值;
根据不同的需要来使用不同的类型的函数,比如这里我的函数就是float类型的,因此用的
float fun(int n);
接下来我来讲讲形参与实参的关系。
实参实际上就是在函数被引用时,用来计算的量。
而形参就是函数在使用的时候,接收实参的值的变量。
但是需要有一个注意的是,我上面引用实参的方式并不会对实参造成影响,比如我想交换两个数的值,而我的操作如下:
#include<stdio.h>
void swap(int x,int y)
{
int z;
z=x;
x=y;
y=z;
}
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("交换前:a=%d b=%d\n",a,b);
swap(a,b);
printf("交换后:a=%d b=%d\n",a,b);
return 0;
}
但是实际上的结果却是:
为什么会这样呢?
其实函数的传参是有两种操作的
第一个就是我上面代码的操作,名为传值操作,第二个就是传址操作,只有当你需要更改实参的时候,才需要用到传址操作。
将上面的代码改成下面就对了:
#include<stdio.h>
void swap(int *x,int* y)
{
int z;
z=*x;
*x=*y;
*y=z;
}
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("交换前:a=%d b=%d\n",a,b);
swap(&a,&b);
printf("交换后:a=%d b=%d\n",a,b);
return 0;
}
代码结果如下:
大家可以看到,在main函数中的n前面有一个&符号,这个符号表示取地址符号,所以这里实际上是传了一个int类型的地址给函数,而函数接收需要int类型的指针,也就是int *;
其交换操作也是如上面代码,需要用
z=*x;
*x=*y;
*y=z;
来更换。
其中,我们还可以将数组的首地址传给函数,因为数组在地址上实际上是相邻的,所以将数组的首地址传给函数,我们就能顺势读取数组的所以数据;
比如我给个问题,将字符串的数据反向输出,列入输入abcde输出edcba;
代码如下:
#include<stdio.h>
void fun(char* arr)
{
if (*arr == '\0')
return;
else
{
fun(arr + 1);
printf("%c", *arr);
}
}
int main()
{
char arr[] = "abcdef";
fun(arr);
return 0;
}
这里我使用了函数递归的操作执行,大家可以看到我用char *接收数组的首地址,然后在函数内部根据条件不同来决定是否继续调用函数,而调用函数的操作相信大家也看到了;
fun(s+1);
这里大家可能会有不同意见,为什么这里不用fun(s++)呢?实际上,s+1和s++有不同,s+1实际上并没有改变s的值,而s++改变了s的值,就会导致地址改变,有的编译器上就会报错。
既然我们已经讲到函数的递归操作了,那我们继续深入讲讲递归是怎么一回事吧。
递归有两个条件,一个是递归的停止条件,一个是每一次调用函数,都必须向递归的停止条件接近,比如我上面的操作,递归的停止操作时如果数组访问到 '\0' 的时候就返回,而每一次调用函数都会使得数组向 '\0' 接近。
函数还有其他操作
比如嵌套调用;
就是在函数中调用其它函数,比如你在main函数使用printf函数,这也是一种嵌套调用。
还有链式操作,就是将函数的返回值作为其他函数的参数;
最典型的就是下面的代码:
#include<stdio.h>
int main()
{
printf("%d",printf("%d",printf("%d",43)));
return 0;
}
结果如下:
这里插入一下其他知识以便大家理解,实际上printf函数是有返回值的,它返回的数字就是输出字符的长度,这条代码最里面的printf函数输出43两个字符,所以第二个输出2一个字符,之后第三个输出1一个字符。
接下来还有一个就是函数声明。
当我们函数定义在main函数下面时,我们需要在main函数上面或者进行函数调用的前面进行函数声明,否则我们的函数是用不了的。
比如:
这里我将代码放在了函数调用下面了,于是编译器就报错了。
还插一句就是,实际上头文件的引用相当于函数声明,比如我在vs里面新建一个项目,然后像下面几张图这样写:
这样我们也能够成功的调用函数。
这样的好处就是能够方便调试,以便以后同事的协作。
以上就是我个人的一些心得,感谢看到这里的各位。