目录
一、函数介绍
函数,也被称作子程序
- C语言中的函数就是一个完成某项特定的任务的一小段代码
- 这段代码是有特殊的写法和调用方法的,由一个或多个语句块组成,具备相对的独立性。
- 函数被分为两类:库函数和自定义函数
二、库函数
2.1 定义
库函数,一般是指编译器提供的可在c源程序中调用的函数。可分为两类,一类是c语言标准规定的库函数,一类是编译器特定的库函数
库函数作为存放在函数库中的函数,具有明确的功能、入口调用参数和返回值,是一段可以重复使用的代码,用来独立地完成某个功能
2.2 使用
在使用某一库函数时,都要在程序中嵌入(用#include)该函数对应的头文件
以下例举一些常用库函数:
需要的包含文件:stdio.h
例如: getchar,putchar,printf,scanf等。
字符串、内存和字符函数。包括对字符串进行各种操作和对字符进行操作的函数。
需要的包含文件:string.h
例如:strcat,strchr,strcmp,strcpy,strlen,strstr等。
数学函数,包括各种常用的三角函数、双曲线函数、指数和对数函数等。
需要的包含文件:math.h
例如:sin,cos,exp(e的x次方),log,sqrt(开平方),pow(x的y次方)等。
动态存储分配,包括"申请分配"和"释放"内存空间的函数。
需要的包含文件:alloc.h或stdlib.h
例如:calloc,free,malloc,realloc等。
时间、日期和与系统有关的函数。
需要的包含文件:time.h
例如:time返回系统的时间;asctime返回以字符串形式表示的日期和时间。
其他的库函数,在使用前如果不知道头文件的话,可以通过百度或者这个网站去自行搜索:
三、自定义函数
3.1 定义
自定义函数顾名思义,就是除库函数外,我们还想拥有实现自己想要的功能的函数,则需要我们自己创建
3.2 构成
基本格式如下:
返回类型 函数名 (输入参数)
{
具体实现
返回值
}
当我们不需要函数来返回值给我们时,则可以这样写:
void 函数名 (输入参数)//void表示函数无返回值
{
具体实现
}
例:实现一个两数相加的函数
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int num1, num2, sum;
printf("请输入两个数字:");
scanf("%d %d", &num1, &num2);
sum = add(num1, num2);
printf("两个数字的和为:%d\n", sum);
return 0;
}
四、函数的参数
4.1 实参(实际参数)
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,真实传给函数,称为实际参数,简称实参
实参可以是常量、变量、表达式、函数等
4.2 形参(形式参数)
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参
形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用
4.3 形参与实参的联系
1. 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误
2. 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参(即形参的值发生改变并不会影响实参)
实例:我们想通过函数交换a,b的值
void swap(int x, int y)
{
int a;
a = x;
x = y;
y = a;
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
printf("a=%d,b=%d", a, b);
return 0;
}
可以看出,在函数内部将形参的值交换之后,实参a和b的值并没有改变
所以形参只是实参的一份临时拷贝
五、函数的调用
5.1 传值调用
顾名思义,传值调用就是直接将实参的值传递给形参
下面是一个计算两数之和的函数,通过return语句返回最后的计算结果:
#include<stdio.h>
int add(int x,int y)
{
return x+y;
}
int main()
{
int a, b;
scanf("%d %d", &a, &b);
int c= add(a,b);
printf("%d",c);
return 0;
}
5.2 传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式
已知在上面交换a,b的值的函数中,我们只通过传递值给形参交换后是不成功的,那么如果我们传递的是地址,又能否交换成功呢?
void swap(int* x, int* y)//用指针来接收地址
{
int a;
a = *x; //*解引用
*x = *y;
*y = a;
}
int main()
{
int a = 10;
int b = 20;
swap(&a, &b);//取a和b的地址给函数
printf("a=%d,b=%d", a, b);
return 0;
}
结果:
这时我们发现a和b的值被成功交换了
即传址调用可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
六、函数的嵌套调用和链式访问
6.1 函数的嵌套调用
定义:在一个大功能的函数内,调用各个小功能的函数。就像一个大的框架,在细分出各个小的框架。
函数嵌套调用的好处之一是可以将一些逻辑划分为更小的部分,并将其封装在内部函数中。这样可以增加代码的可读性和可维护性
举例:
#include <stdio.h>
void innerFunction() {
printf("这是内部函数\n");
}
void outerFunction() {
printf("这是外部函数,将调用内部函数\n");
innerFunction();
}
int main() {
printf("这是主函数,将调用外部函数\n");
outerFunction();
return 0;
}
当我们运行这个程序时,它会依次执行main
、outerFunction
和innerFunction
函数,并输出以下结果:
这是主函数,将调用外部函数 这是外部函数,将调用内部函数 这是内部函数
6.2 函数的链式访问
即把一个函数的返回值作为另一个函数的参数
举例:
int main()
{
printf("%d",printf("%d",printf("%d",4321)));
return 0;
}
执行结果:
因为printf函数的返回值是打印的数字的个数,所以最里面即最右边的printf先执行,打印完后返回给中间的printf的参数为4,则再打印一个4,同时又给最左边的printf函数返回参数1,最后打印1
七、函数的声明和定义
7.1 函数的声明
函数的声明是指在代码中提前定义函数的名称、参数和返回值类型,以便在其他地方使用
- 函数的声明以便编译器能够在调用函数时检查参数的类型和返回值的类型是否匹配
- 在使用函数之前,需要先进行函数声明
- 函数的声明一般要放在头文件中
7.2 函数的定义
函数的定义一般包括函数声明和函数实现两部分
- 函数声明是在代码中声明函数的存在,包括函数名、参数列表和返回值类型
- 函数实现是函数的具体实现代码
- 函数的声明和实现可以分开写在不同的文件中,但是需要保证声明和实现的一致性
八、函数递归
8.1 定义
函数递归是指在函数内部调用自身的过程
递归函数通常包含两个部分:基本情况和递归情况
基本情况是指递归函数停止调用自身的条件,用来终止递归过程。递归情况是指递归函数中调用自身的部分,用来迭代解决问题
基本思想:
- 将一个复杂的问题分解成更小规模的相同问题,然后递归地解决这些子问题
- 每次递归调用都会将问题的规模减小,直到达到基本情况,从而得到最终的解
8.2 示例
#include<stdio.h>
int factorial(int n) {
if (n == 0)
return 1;
else
return n * factorial(n-1);
}
这个示例中函数将调用自身来计算(n-1)的阶乘,并将结果乘以n返回
当第一个传入的n为5时,程序就会进行5*4*3*2*1的过程,返回最终结果
九、总结
通过这篇文章,我们学了关于函数的相关知识,包括:库函数、自定义函数、函数的参数和调用、函数的嵌套调用和链式访问、函数的声明和定义、函数的递归。希望大家可以熟练掌握并运用!