函数的基本概念
1.函数的概念
函数是一个可以反复使用的程序段,从其他的程序段中,均可以通过条约定的语句来执行这段代码,完成既定的工作。
在C语言中我们一般会见到两类函数:
- 库函数
- 自定义函数
2.库函数
2.1标准库和头文件
C语言标准中规定了C语言的各种语法规则,C语言并不提供库函数;C语言的国际标准ANSI C规定了一
些常用的函数的标准,被称为标准库,那不同的编译器厂商根据ANSI提供的C语言标准就给出了一系列
函数的实现。这些函数就被称为库函数。
有了库函数,一些常见的功能就不需要程序员自己实现了,一定程度提升了效率;同时库函数的质量和执行效率上都更有保证。
各种编译器的标准库中提供了一系列的库函数,这些库函数根据功能的划分,都在不同的头文件中进行了声明。
库函数的相关文件: link
2.2库函数的使用方法
库函数的学习和查阅,比如:
C/C++ 官方的链接: link
cplusplus.com:link
举例: sqrt
double sqrt(double x);
有用信息:
- sqrt 是函数名。
- x 是函数的参数,表示调用 sqrt 函数需要传递一个 double 类型的值。
- double 是返回值类型的值 - 表示函数计算的结果是 double 类型的值。
2.2.1功能
Compute square root x 计算平方根
Returns the square root x. (返回平方根)
2.2.2头文件包含
库函数是在标准库中对应的头文件中声明的,所以库函数的使用,务必包含对应的头文件,不包含是可能会出现一些问题的。
2.2.3实践
#include<stdio.h>
#include<math.h>// sqrt() 的头文件
int main()
{
double a = 16.0;//在 VS2022中,16.0认为是 double 类型
double b = sqrt(a);
printf("%lf", b);// double 类型用 %lf 占位符来接收
return 0;
}
输出结果:
2.2.4库函数文档的一般格式
- 函数原型
- 函数功能介绍
- 参数和返回类型
- 代码举例
- 代码输出
- 相关知识链接
3.自定义函数
3.1函数的语法形式
自定义函数和库函数是一样的,形式如下:
ret_type fun_name(形式参数)
{
}
- ret_type 是函数的返回类型
它是用来表示计算结果的类型,有时候返回类型可以是 void 表示什么都不返回。
- fun_name 是函数名
他是为了方便使用函数,尽量要根据函数的功能起的有意义。
- 括号放的是形式参数
函数的参数也可以是 void 明确表示函数没有参数。如果有参数,要交代清楚参数的类型和名字,以及参数个数。
- { } 是函数体
它是为了完成计算的过程。
3.2函数的举例
例子:
写一个加法,完成 2 个整型变量的加法操作
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
//输入
scanf("%d %d", &a, &b);
//调用加法函数,完成 a和b 的相加
//求和的结果放在 r 中
//输出
printf("%d\n", r);
return 0;
}
根据要完成的的要求:
函数取名为 :Add ,函数 Add 需要接收 2 个整型类型的参数,计算的结果也是整型。
所以:
#include<stdio.h>
int Add(int a, int b)
{
int z = 0;
z = a + b;
return z;
}
int main()
{
int a = 0;
int b = 0;
//输入
scanf("%d %d", &a, &b);
//调用加法函数,完成 a和b 的相加
//求和的结果放在 r 中
int r = Add(a, b);
//输出
printf("%d\n", r);
return 0;
}
Add 函数也可以简化为:
int Add(int a, int b)
{
return a + b;
}
函数参数部分需交代清楚: 参数个数,每个参数的类型是什么,形参的名字是什么。
4.形参和实参
在函数使用的过程中,吧函数的参数分为,实参和形参。
看一下前面的代码:
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int a = 0;
int b = 0;
//输入
scanf("%d %d", &a, &b);
//调用加法函数,完成 a和b 的相加
//求和的结果放在 r 中
int r = Add(a, b);
//输出
printf("%d\n", r);
return 0;
}
4.1实参
在上面的代码中,第 2~6行是 Add 函数的定义,有了函数后,在16行调用 Add 函数的。
把第16行调用 Add 函数时,传递给函数的参数 a 和 b 称为实际参数,简称实参。实际参数就是真实传递给函数的参数。
4.2形参
在上面的代码中,第3行定义函数时,在函数名 Add 后的括号中写的 x 和 y ,被称为形式参数,简称形参。
为什么叫形式参数?
- 实际上,如果只是定义了 Add 函数,而不去调用的话, Add 函数的参数 x 和 y 只是形式上存在的,不会向内存申请空间,不会真实存在的,所以叫形式参数。
- 形式参数只有在函数被调用的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形参的实例化。
4.3实参和形参的关系
虽然我们提到了实参是传递给形参的,他们之间是有联系的,但是形参和实参各自是独立的内存空间。
这个现象可以通过调试来观察:
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 0;
int b = 0;
//输入
scanf("%d %d", &a, &b);
//调用加法函数,完成 a和b 的相加
//求和的结果放在 r 中
int r = Add(a, b);
//输出
printf("%d\n", r);
return 0;
}
我们在调试的时候可以观察到,x 和 y 确实得到了a 和 b 的值,但是 x 和 y 的地址和a和b的地址是不一样的,所以我们可以理解为形参是实参的一份临时拷贝。
5.return 语句
• return 后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。
• return后边也可以什么都没有,直接写 return ; 这种写法适合函数返回类型是 void 的情况。
• return 返回的值和函数返回类型不一致,系统会自动将返回的值隐式转换为函数的返回类型。
• return 语句执行后,函数就彻底返回,后边的代码不再执行。
• 如果函数中存在 if 等分支的语句,则要保证每种情况下都有 return 返回,否则会出现编译错误。
6.数组做函数参数
在使用函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进行操作。
例子:
写一个函数对将一个整型数组的内容,全部置为 -1,再写一个函数打印数组的内容。
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//设置数组内容为 1
set_arr();
//打印数组内容
print();
return 0;
}
分析:
- 这里的 set_arr 函数要能够对数组内容进行设置,就得把数组作为参数传递给函数,同时函数内部在设置数组每个元素的时候,也得遍历数组,需要知道数组的元素个数。
- 所以我们需要给 set_arr传递 2个参数,一个是数组,另外⼀个是数组的元素个数。仔细分析 print_arr 也是⼀样的,只有拿到了数组和元素个数,才能遍历打印数组的每个元素。
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//求数组 arr 元素的个数
int sz = sizeof(arr) / sizeof(arr[0]);
//设置数组内容为 1
set_arr();
//打印数组内容
print_arr();
return 0;
}
数组作为参数传递给了 set_arr 和 print_arr 函数了,那这两个函数应该如何设计呢?
插入一个知识点:与数组传参相关
- 函数的形式参数要和函数的实参个数匹配
- 函数的实参是数组,形参也是可以写成数组形式的
- 形参如果是一维数组,数组大小可以省略不写
- 形参如果是二维数组,行可以省略,但是列不能省略
- 数组传参,形参是不会创建新的数组的
- 形参操作的数组和实参的数组是同一个数组
根据上述的信息,我们就可以实现这两个函数:
#include<stdio.h>
void set_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
arr[i] = -1;
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//求数组 arr 元素的个数
int sz = sizeof(arr) / sizeof(arr[0]);
//设置数组内容为 1
set_arr(arr,sz);
//打印数组内容
print_arr(arr,sz);
return 0;
}
结果: