函数的本质是教计算机做一件事,也就是把一组指令进行捆绑,并且给这组指令起一个名字。函数可以有输入和输出,增加了函数的普适性。
函数思维是一种模块化的思维方式,帮助我们把一个功能独立出来,增加了代码的复用性,并且大大简化了代码编写。
函数的创建包括函数声明和函数定义。
函数的声明只需要写明函数的返回值类型、函数名和参数即可,要写在调用和定义之前,一般把函数声明写在主程序之前。如果在声明函数的时候将函数的定义补全,则无需再单独写函数的定义。
函数的声明必须包括:
// 函数的声明
返回值类型 函数名(参数1类型 参数1,参数2类型 参数2, ……);int my_max(int a, int b);函数定义需要写明函数的具体执行内容。如果在函数声明中写明了函数的具体执行内容,则不需要再写函数的定义。
函数的定义包括:
1// 函数的声明
2返回值类型 函数名(参数1类型 参数1,参数2类型 参数2, ……);
3
4
5// 函数的定义
6返回值类型 函数名(参数1类型 参数1,参数2类型 参数2, ……) {
7 函数具体执行的代码
8
9 return 返回值
10}1// 函数声明
2int my_max(int a, int b);
3
4// 函数定义
5int my_max(int a, int b) {
6 if (a > b) {
7 return a;
8 }
9 return b;
10}函数的返回值类型可以是:
函数可以没有返回值,没有返回值的函数请用void表示。
函数的命名规则与变量相同。一般情况下函数不能重名,除非是完成同一功能且参数不同(参数数量不同、参数类型不同)时才可以重名,此时这些函数被称为对某个函数名称的重载。
函数使用return语句进行返回,一旦运行return语句后,return语句后面的代码将不再执行。
void函数写return语句时,直接写return;即可,同时void函数可以不写return语句,如果不写return语句编译器会自动补全一句return;。
调用函数时可直接使用函数名加括号的形式进行调用,对于有参数的函数,需要在括号内填写上实际的参数。
1#include <bits/stdc++.h>
2using namespace std;
3
4// 函数声明
5int my_max(int a, int b);
6
7int main() {
8 int num1, num2;
9 cin >> num1 >> num2;
10 int max_number = my_max(num1, num2);
11 cout << max_number << endl;
12}
13
14// 函数定义
15int my_max(int a, int b) {
16 if (a > b) {
17 return a;
18 }
19 return b;
20}如果几个函数的函数名称相同,但是参数不同(参数数量不同、参数类型不同),那么这些函数被称为对某个函数名称的重载。
以下两个函数都是返回两个数中较小的那个数,但是第一个函数是用来比较int型数据的,而第二个函数则是用来比较double型数据的。
1#include <iostream>
2//#include <bits/stdc++.h>
3using namespace std;
4
5double min(int num1, int num2);
6double min(double num1, double num2);
7
8int main() {
9 cout << min(2, 4) << endl;
10 cout << 3 / min(2, 4) << endl;
11 cout << min(0.2, 0.8) << endl;
12// cout << min(0.2, 2) << endl;
13 return 0;
14}
15
16double min(int num1, int num2) {
17 if (num1 < num2) {
18 return num1;
19 }
20 return double(num2);
21}
22
23double min(double num1, double num2) {
24 if (num1 < num2) {
25 return num1;
26 }
27 return num2;
28}函数调用的完整流程可以用下图表示:
下面以"纯粹合数"问题为例,演示函数是如何被调用和返回的。
问题:判断一个数的每一位数字是否都是合数(4、6、8、9),如果是则称为纯粹合数。
1// 判断一个数字是否是合数位(4、6、8、9)
2bool isCompositeDigit(int d) {
3 return d == 4 || d == 6 || d == 8 || d == 9;
4}
5
6// 判断一个数是否是纯粹合数
7bool isPureComposite(int n) {
8 while (n > 0) {
9 int d = n % 10;
10 if (!isCompositeDigit(d)) {
11 return false;
12 }
13 n = n / 10;
14 }
15 return true;
16}执行过程(n = 468):
| 步骤 | 函数 | n | d | isCompositeDigit(d) | 动作 |
|---|---|---|---|---|---|
| 1 | isPureComposite | 468 | 8 | 调用 → true | 继续 |
| 2 | isPureComposite | 46 | 6 | 调用 → true | 继续 |
| 3 | isPureComposite | 4 | 4 | 调用 → true | 继续 |
| 4 | isPureComposite | 0 | - | - | 循环结束,return true |
执行过程(n = 462):
| 步骤 | 函数 | n | d | isCompositeDigit(d) | 动作 |
|---|---|---|---|---|---|
| 1 | isPureComposite | 462 | 2 | 调用 → false | return false(提前结束) |
关键理解: 函数调用就像"跳转"——主程序跳到函数中执行,函数执行完后跳回主程序继续。每次调用函数时,参数会被赋予新的值,函数内部的变量不会影响外部。
信号1:同一段代码出现了多次。 如果你发现自己在复制粘贴代码,说明应该把这段代码提取成一个函数。
信号2:逻辑可以独立。 例如"判断是否为素数"、"求最大公约数"、"交换两个数"等操作,逻辑完整且独立,适合封装成函数。
信号3:代码太长难以阅读。 当 main 函数超过 50 行时,考虑将其中的逻辑拆分成多个函数,每个函数完成一个明确的任务。
设计函数的步骤:
int mymax(int a, int b);,定义时写成 int mymax(int a, double b) {...},参数类型不同会导致编译器认为这是两个不同的函数。strcpy(src, dst) 写成了 strcpy(dst, src)(虽然这个例子中 strcpy 的参数顺序本身就是 dst 在前),在自定义函数中更容易犯这种错误。