前言
在阅读过程中有任何问题都可以发布到评论区,有价值的问题将会放到文章末尾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;
void fun1(){ ...; }
double fun2(char a, char b){ double ans; ...; 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(); cout << "调用 fun2(): " << endl; int a = 1; fun2(a); fun2(2); cout << "调用 fun3(): " << endl; 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(){ ...; 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;
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 语句直接结束函数的执行。
示例
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){ if(n <= m) return ; cout << "满足题目条件" << endl; int t; t = n; n = m; m = t; }
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 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; }
|
递归
在一个函数内部调用自己的过程叫做递归。
但是递归调用自己的过程中要注意写明终止条件,否则会一直调用下去。
示例
递归实现阶乘。
解题思路: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){ if(n <= 1) return 1; return n * fun(n - 1); }
int main(){ int res = fun(5); cout << res << endl; return 0; }
|
Q&A