C++ 函数

前言

本文记录 C++ 相关的函数的相关知识。

在阅读过程中有任何问题都可以发布到评论区,有价值的问题将会放到文章末尾Q&A之中!

函数示例

函数实现Hello World

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std;

void fun(){
cout << "Hello World!" << "\n";
}

int main(){
fun();
return 0;
}

函数实现阶乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

int fun1(int n){
int res = 1;
for(int i = 1; i <= n; i ++ ){
res *= i;
}
return res;
}

int main(){
int num = 5;

int ans = fun1(num);

cout << ans << endl;
return 0;
}

函数的定义

函数的类型

首先需要声明函数类型,其次需要写明函数名【任意起名】。

​ 函数的类型声明了该函数需要返回的值是什么类型

​ 注意函数的类型和变量的类型相比多一个void类型,即空类型,当函数定义为void类型,不需要返回值

​ 当函数类型不为 void 类型时,必须要有和函数类型相同的返回值。在函数内部使用 return 返回。

函数的参数

在函数名之后需要加一个(),括号内写明函数所需参数

​ 注意不是所有的函数都需要参数,也就是说,函数的声明的括号内的参数可以为空。

​ 参数就是各种类型的变量,但是声明参数的过程和变量定义的过程有一些不同。

变量定义时,多个同类型变量可以用逗号 , 隔开定义即可。

函数的参数的声明即使是同类型的参数,也必须多次声明参数类型,每次声明用,隔开。

函数体

之后需要将函数的具体内容【函数体】 用 {} 括起来。

​ 与判断和循环不同的是,判断和循环内部如果只有一行代码,可以省略 {}

​ 但是函数就算只有一行代码也必须写 {}

返回值

最后在函数体内部如果不是空类型,必须有返回值。如果不写返回值,返回的有可能是一个随机数。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

using namespace std;

// 定义一个返回类型为空类型的函数
// 函数名:fun1,没有参数
void fun1(){
...;
// 空类型函数无需 return
}

// 定义一个返回类型为双精度浮点数类型的【double】的函数
// 函数名:fun2,有两个参数,均为 char 类型,每个参数的定义都需要写明类型并且用 , 隔开
double fun2(char a, char b){
double ans;
...;
// 函数类型是 double 类型,需要返回一个 double 类型的值
return ans;
}

int main(){

return 0;
}

函数的调用

空类型无参函数调用时直接写明函数名,后面加上 () 即可。

如果函数有参数的话,需要在 () 内部给定同类型的参数,该参数可以是同类型的变量,或者直接赋值都可以。

其次看函数的类型是什么,如果不是空类型,那么则需要定义一个同类型的变量来接受返回值,也可以直接进行输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>

using namespace std;

void fun1(){
cout << "Hello World!" << endl;
}

void fun2(int n){
cout << "n = " << n << endl;
}

int fun3(int n, int m){
int res = n * m;
return res;
}

int main(){

cout << "调用 fun1(): " << endl;
// fun1 为空类型,同时没有参数,调用直接写明函数名和()即可
fun1();

cout << "调用 fun2(): " << endl;
// fun2 为空类型,没有返回值。
// 同时有一个int类型的参数,需要在()传递一个 int 类型的变量或者 int 类型的值
int a = 1;
fun2(a);
fun2(2);

cout << "调用 fun3(): " << endl;
// fun3 为 int 类型,需要一个 int 类型的变量来接收返回值
// 同时有两个 int 类型,需要在()内传递两个 int 类型的变量或者 int 类型的值
int c = 3, d = 4;
int e = fun3(c, 5);
cout << "e = " << e << "\nf = " << fun3(6, d) << endl;
return 0;
}

一般来说主函数只能调用其他函数,不能被其他函数调用。

除主函数外,任何一个函数都可以调用任何一个函数,包括自己。

函数的声明

声明

在程序中,也可以先对函数进行声明,之后在写明具体的函数体内容

如同在定义变量的过程中,我们可以先定义变量,再进行赋值

在函数声明的过程中,函数的参数的参数名不是必须的,但是参数类型是必须的

也就是说,参数名可以不写,但是类型必须声明

在函数声明的最后,需要用 ; 结束声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

using namespace std;

// 函数的声明
int fun1(int ,int );

// 函数的定义
int fun1(int n, int m){
...;
}

int main(){
...;
return 0;
}

用法

如果不进行函数声明,那么被调用的函数一定要定义在调用函数的上面

即如果 A 函数中要调用 B 函数,那么 B 函数必须定义在 A 函数之上。

错误写法:这种写法会导致 fun1 找不到 fun2 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

using namespace std;

void fun1(){
...;
// 在 fun1 函数中调用 fun2 函数失败
fun2();
}

void fun2(){
...;
}

int main(){
...;
fun1();
return 0;
}

正确写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

using namespace std;

// 被调用的 fun2 要写在调用 fun2 的 fun1 之上
void fun2(){
...;
}

void fun1(){
...;
fun2();
}

int main(){
...;
fun1();
return 0;
}

如果进行了函数的声明,那么函数的位置就没有要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

void fun1();
void fun2();

void fun1(){
...;
fun2();
}

void fun2(){
...;
}

int main(){
...;
fun1();
return 0;
}

函数的执行顺序

执行顺序

所有的C++代码都是以主函数【main函数】程序的入口的。

函数的调用都是从主函数中开始调用的。

在程序执行的过程中,当调用了一个函数,则必须完成该函数的内容之后才可以继续执行调用语句之后的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

using namespace std;

int fun2(int n){
cout << "进入fun2():" << endl;
int m = n * n;
cout << "fun2 执行结束" << endl;
return m;
}

void fun1(){
cout << "进入fun1():" << endl;
int res = fun2(5);
cout << "res = " << res << endl;
cout << "fun1 执行结束" << endl;
}

int main(){
cout << "调用fun1():" << endl;
fun1();
cout << "完成调用fun1()" << endl;
return 0;
}

在计算机内部,有一种常用的数据结构叫做

栈的特点是从一端实现数据的出入,先入栈的数据后出栈,后入栈的数据先进栈。【先进后出】

函数的调用过程就是一个入栈出栈的过程。

每次调用一个函数都会将该函数入栈,在该函数执行完成之后该函数出栈

之后调用这个函数的函数才会继续执行。

栈中最底层的是 main 函数

参数的类型

传值参数

当函数内初始化一个非引用类型的变量时, 调用时所传递的值会拷贝到该变量上。

此时,在函数内部改变该参数的值不会影响到调用时原变量的初始值

示例

在下面这个程序中,函数内定义了一个参数 n

在主函数中调用 fun1 函数时,原函数传递的 a 的值为 5.

在 fun1 函数中将接收到的参数值 n 改变为 100

但是在 fun1 调用完成之后原函数中 a 的值仍然为 5,没有变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

using namespace std;

int fun1(int a){
cout << "接收到的值:" << a << endl;
cout << "函数中改变参数值为100" << endl;
a = 100;
return a;
}

int main(){
int a = 5;

cout << "调用函数:" << endl;

int ans = fun1(a);
cout << "函数返回值:" << ans << endl;

cout << "调用完成" << endl;

cout << "原函数中 a = " << a << endl;

return 0;
}

引用参数

当函数内初始化一个引用类型的变量时。

此时,在函数内部改变该参数的值会影响到调用时原变量的初始值

引用的方法就是在函数定义时在参数前面加一个地址符&

示例

在下面这个程序中,函数内定义了两个参数n, m

在主函数中调用 fun1 函数时,传递a, b 的值为 3, 4.

在 fun1 函数中将接收到的参数值 n, m 改变为 10, 20

但是在 fun1 调用完成之后原函数a, b 的值也变化为了 10, 20

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

using namespace std;

void fun1(int &n, int &m){
cout << "接收到的 n = " << n << " m = " << m << endl;
cout << "函数中改变参数值为 10,20" << endl;
n = 10, m = 20;
}

int main(){
int a = 3, b = 4;

cout << "调用函数:" << endl;
fun1(a, b);
cout << "调用完成" << endl;
cout << "原函数中:a = " << a << " b = " << b << endl;
return 0;
}

使用引用传参可以改变原函数中的值的原因是元素在内存中就是存放到一片地址当中的。

在使用引用传递时,传递的是该变量的地址

所以改变函数中的值时是直接对该地址进行操作,所以原函数中也会改变。

而非引用时只传递了值,没有传递地址,相当于复制粘贴了一份,所以函数中的变化不会影响到原函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

void fun1(int n){
cout << "fun1 中 n 的地址为: " << &n << endl;
}

void fun2(int &n){
cout << "fun2 中 n 的地址为: " << &n << endl;
}

int main(){
int a = 30;
cout << "a 的值为:" << a << endl;
cout << "原函数中 a 的地址为:" << &a << endl;
fun1(a);
fun2(a);
return 0;
}

数组参数

当参数为数组时,默认为引用参数

即在函数内部改变数组的值时,会影响到原函数中的数组值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

using namespace std;

void fun(int a[5]){
cout << "函数中改变数组值为 1" << endl;
for(int i = 0; i < 5; i ++){
a[i] = 1;
}
}

int main(){
int a[5] = {1, 2, 3, 4, 5};

cout << "调用函数前 a 数组的值:";
for(int i = 0; i < 5; i ++ ){
cout << a[i] << ' ';
}
cout << endl;

cout << "调用函数" << endl;
fun(a);
cout << "调用结束" << endl;

cout << "调用函数后 a 数组的值:";
for(int i = 0; i < 5; i ++ ){
cout << a[i] << ' ';
}
cout << endl;
return 0;
}

一维数组

当函数的参数类型为数组时,有三种定义方法。

  • 定义为确定长度数组
  • 定义为无长度数组
  • 定义为指针类型

在函数调用的过程中传递数组类型参数时,直接传递数组名即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

using namespace std;

// 确定长度数组
void fun1(int a[5]){
...;
}

// 无长度数组
void fun2(int a[]){
...;
}

// 指针类型
void fun3(int *a){
...;
}

int main(){
int a[5] = {1, 2, 3, 4, 5};

// 函数调用直接传递数组名即可
fun1(a);
fun2(a);
fun3(a);

return 0;
}

多维数组

当函数的参数类型为多维数组时,基本的规则和一维数组相同。

但是在多维数组中,想要不填写长度只能将第一维长度省略其余维度都必须声明长度

同样想要使用指针定义参数时,只能将第一维用指针表示,同时用[]括起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

using namespace std;

// 确定长度数组
void fun1(int a[3][4]){
...;
}

// 无长度数组,只能省略第一维长度
void fun2(int a[][4]){
...;
}

// 指针传递,只能将第一维写为指针形式
void fun3(int (*a)[4]){
...;
}

int main(){
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
fun1(a);
fun2(a);
fun3(a);
return 0;
}

返回类型

函数中一般使用 return 语句来结束函数的执行,其作用相当于循环中的 break 语句

在执行 return 语句之后,程序会返回到调用函数之后的一句语句继续执行。

无返回值函数

void 类型函数可以不写 return 语句,因为在 void 类型中会在函数末尾自动执行一个 return 语句。

但是 void 类型也可以通过手动写一个 return 语句直接结束函数的执行。

示例

如果 a > b 交换 a 和 b。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

using namespace std;

// 要实现主函数中的元素值也交换,需要定义为引用参数
void fun(int &n, int &m){
// 如果 n <= m,则不满足题目要求,无需操作,直接结束函数 return 即可。
if(n <= m) return ;

cout << "满足题目条件" << endl;
// 如果上述判断没有执行,说明满足题目要求,按照题目要求交换。
int t;
t = n;
n = m;
m = t;

// 函数末尾无需 return ,默认函数结束返回
// return
}

int main(){
int a, b;
cin >> a >> b;
fun(a, b);
cout << a << " " << b << endl;
return 0;
}

有返回值函数

有返回值函数必须写 return 语句,且返回类型必须和函数类型相同,同时在调用函数时也需要一个同类型的变量接收返回值

如果不写 return 语句不会报错,但是返回的值不确定,是一个随机值。

示例

函数实现阶乘。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

using namespace std;

// 函数类型为 int 类型,必须返回一个 int 类型的变量
int fun1(int n){
int res = 1;
for(int i = 1; i <= n; i ++ ){
res *= i;
}
// 返回值为一个 int 类型的变量,和函数定义类型相同
return res;
}

int main(){
int num = 5;

// 调用函数时也需要定义一个函数定义类型的变量来接收返回值。
int ans = fun1(num);
cout << ans << endl;
return 0;
}

递归

在一个函数内部调用自己的过程叫做递归。

但是递归调用自己的过程中要注意写明终止条件,否则会一直调用下去。

示例

递归实现阶乘。

解题思路:n! = n * (n - 1)!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

// 递归实现阶乘,从大到小逐个数字相乘
int fun(int n){
// 因为 1 的阶乘确定为 1,所以使用 1 为终止条件
// 当参数为 1 时,返回值也为 1
if(n <= 1)
return 1;

// 每次调用都调用比当前数值小 1 作为参数进行传递
// 解题思路:n! = n * (n - 1)!
return n * fun(n - 1);
}

int main(){
int res = fun(5);
cout << res << endl;
return 0;
}

Q&A