Skip to content

函数具有一定功能的独立的代码模块。每个C++程序都至少有一个函数!即main函数。常用的头文件中为我们封装了一些函数,如cmath中的sqrtabs等,使用它们使我们的编码变得轻松愉快。当然,我们也可以自定义函数,以实现特定的功能需求。

1 函数的声明和定义

1.1 函数的定义

C++中函数定义的一般形式如下:

return_type function_name(parameter_list) {
	body of the function
}

C++中,函数由函数头函数体(执行特定任务的语句块)组成,函数头包括:

  • 返回值类型:函数返回值的类型,如果什么都不返回类型为void
  • 函数名称:函数名称和参数列表构成了函数签名。
  • 参数列表:调用函数时向函数传递的参数(实参),函数参数分为实参形参。参数列表可以为空。

示例

cpp
int my_max(int a, int b) {
	int c;
	if (a > b) {
		c = a;
	} else {
		c = b;
	}
	return c;
}
// 或
void print() {
	cout << "Hello World!" << endl;
}
  • 注:先定义函数,再调用函数!除非你在调用函数前写了函数声明。

1.2 函数声明

把函数定义去掉函数体,并在括号后添加;就是函数声明。函数声明用来告诉编译器函数名称以及如何调用函数。

形式如下:

return_type function_name(parameter_list);

如上例中的函数声明是:

cpp
// 1
int max(int a, int b);
// 或者写成
int max(int, int);

// 2
void print();
  • 在函数声明中,参数的名称并不重要,只有参数的类型是必须的。

函数声明是必须的吗?我应该什么时候写函数声明?

函数声明不是必须的。如果函数定义在调用函数之前则不必写函数声明,如果函数定义在调用函数之后,为了告诉编译器调用函数的相关信息,我们需要在调用函数时给出函数声明,这时函数定义可以写在任何地方。

2 调用函数

程序通过函数名来调用函数。当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数执行结束(遇到return语句或者是函数结束的右花括号)会把程序的控制权交还给主程序(main)

函数调用时如果需要传递参数,这时传递的值被称为实参(函数定义时的参数被称为形参)。实参和形参需要严格对应:个数、顺序、类型。

示例:

cpp
#include <iostream>
using namespace std;

// 函数声明
int max(int a, int b); // 形参

int main () {
	// 局部变量声明
	int a = 100;
	int b = 200;
	int c = 300;

	// 函数返回值作为函数参数、表达式的一部分
	// 求三个数中的最大值
	int ret = max(max(a, b), c); // 实参

	cout << "Max value is : " << ret << endl;

	return 0;
}

// 函数返回两个数中较大的那个数
int max(int a, int b) {
	// 局部变量声明
	int result;

	if (a > b)
	  result = a;
	else
	  result = b;

	return result;
}
  • return语句会终止函数调用
  • main函数的调用者是操作系统,如果程序成功执行,返回值是0,如果程序出了问题那么返回值就是非零值。

2.1 函数参数的传递方式

如果函数需要参数,则此时会发生参数传递过程。在这个过程中接受参数值的变量称之为形式参数,而传递的值被称为实际参数。形式参数就像函数内的其他局部变量,进入函数时被创建,退出函数时被销毁,如果不想被销毁,我们就需要改变C++默认的参数传递方式!

C++有两种种参数传递方式

  1. 传值调用:该方法把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
  2. 传址调用:该方法把参数的地址或引用赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

2.1.1 传值调用

cpp
void swap(int a, int b) {
	int t = a;
	a = b;
	b = t;
}

int main() {
	int a = 10, b = 9;
	swap(a, b);
	cout << "a = " << a << "b = " << b << endl;

	return 0;
}

2.1.2 传址调用

cpp
void swap(int& a, int& b) {
	int t = a;
	a = b;
	b = t;
}

int main() {
	int a = 10, b = 9;
	swap(a, b);
	cout << "a = " << a << " b = " << b << endl;

	return 0;
}

引用:&

int a = 10; int& b = a; b += 10;这里的ba是一样的,都指向同一个内存单元,修改ba也会跟着变。可以把b看作a的别名。

3 函数的默认参数

当您定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。

这是通过在函数定义中使用赋值运算符来为参数赋值的。调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值。请看下面的实例:

cpp
#include <iostream>
using namespace std;

int sum(int a, int b = 20) {
	int result;

	result = a + b;

	return result;
}

int main () {
	// 局部变量声明
	int a = 100;
	int b = 200;
	int result;

	// 调用函数来添加值
	result = sum(a, b);
	cout << "Total value is :" << result << endl;

	// 再次调用函数
	result = sum(a);
	cout << "Total value is :" << result << endl;

	return 0;
}

4 简单例题

4.1 距离函数

题目描述

给出平面坐标上不在一条直线上三个点坐标 (x1,y1),(x2,y2),(x3,y3),坐标值是实数,且绝对值不超过 100.00,求围成的三角形周长。保留两位小数。

对于平面上的两个点 (x1,y1),(x2,y2),则这两个点之间的距离 dis=(x2x1)2+(y2y1)2

输入格式

输入三行,第 i 行表示坐标 (xi,yi),以一个空格隔开。

输出格式

输出一个两位小数,表示由这三个坐标围成的三角形的周长。

样例 #1

样例输入 #1
0 0
0 3
4 0
样例输出 #1
12.00

提示

数据保证,坐标均为实数且绝对值不超过 100,小数点后最多仅有 3 位。

cpp
#include <bits/stdc++.h>

using namespace std;

float dist(float x1, float y1, float x2, float y2) {
	return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

int main() {
	float x1, x2, x3, y1, y2, y3;
	cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;

	printf("%.2f\n", dist(x1, y1, x2, y2) + dist(x1, y1, x3, y3) + dist(x2, y2, x3, y3));

	return 0;
}

4.2 歌唱比赛

题目描述

n(n100) 名同学参加歌唱比赛,并接受 m(m20) 名评委的评分,评分范围是 010 分。这名同学的得分就是这些评委给分中去掉一个最高分,去掉一个最低分,剩下 m2 个评分的平均数。请问得分最高的同学分数是多少?评分保留 2 位小数。

输入格式

第一行两个整数 n,m
接下来 n 行,每行各 m 个整数,表示得分。

输出格式

输出分数最高的同学的分数,保留两位小数。

样例 #1

样例输入 #1
7 6
4 7 2 6 10 7
0 5 0 10 3 10
2 6 8 4 3 6
6 3 6 7 5 8
5 9 3 3 8 1
5 9 9 3 2 0
5 8 0 4 1 10
样例输出 #1
6.00
cpp
#include <bits/stdc++.h>

using namespace std;

const int N = 110, M = 25;
float s[N], a[M];

float score(float t[], int len) {
	float maxv = -1, minv = 11, sum = 0;
	for (int i = 0; i < len; i++) {
		maxv = max(maxv, t[i]);
		minv = min(minv, t[i]);
		sum += a[i];
	}

	return (sum - maxv - minv) / (len - 2);
}

int main() {
	int n, m; cin >> n >> m;
	float ans = -1;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> a[j];
		}
		ans = max(ans, score(a, m));
	}

	printf("%.2f\n", ans);

	return 0;
}

4.3 计算阶乘

题目描述

n!,也就是 1×2×3×n

挑战:尝试不使用循环语句(for、while)完成这个任务。

输入格式

第一行输入一个正整数 n

输出格式

输出一个正整数,表示 n!

样例 #1

样例输入 #1
3
样例输出 #1
6

提示

数据保证,1n12

cpp
#include <bits/stdc++.h>

using namespace std;

long long fact(int n) {
	long long ans = 1;
	for (int i = 2; i <= n; i++) {
		ans *= i;
	}

	return ans;
}

int main() {
	int n; cin >> n;

	cout << fact(n) << endl;

	return 0;
}

// 不使用循环
#include <bits/stdc++.h>

using namespace std;

long long fact(int n) {
	if (n == 1 || n == 0) return 1;
	return n * fact(n - 1);
}

int main() {
	int n; cin >> n;

	cout << fact(n) << endl;

	return 0;
}

4.4 递归拓展-赦免战俘

  • 有点问题
cpp
#include <bits/stdc++.h>

using namespace std;

const int N = 1050;
int a[N][N];

void release(int n, int x, int y) {
	if (n == 2) {
		a[x][y] = 0;
		return;
	}

	for (int i = x; i <= x + n / 2 - 1; i++) {
		for (int j = y; j <= y + n / 2 - 1; j++) {
			a[i][j] = 0;
		}
	}
	release(n / 2, x, y + n / 2);
	release(n / 2, x + n / 2, y);
	release(n / 2, x + n / 2, y + n / 2);
}

int main() {
	memset(a, -1, N * N);

	int n; cin >> n;

	n = pow(2, n);
	release(n, 1, 1);

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (a[i][j] == -1) cout << 1 << " ";
			else cout << a[i][j] << " ";
		}
		cout << endl;
	}

	return 0;
}

5 习题讲解

5.1 闰年展示

题目描述

输入 x,y,输出 [x,y] 区间中闰年个数,并在下一行输出所有闰年年份数字,使用空格隔开。

输入格式

输入两个正整数 x,y,以空格隔开。

输出格式

第一行输出一个正整数,表示 [x,y] 区间中闰年个数。

第二行输出若干个正整数,按照年份单调递增的顺序输出所有闰年年份数字。

样例 #1

样例输入 #1
1989 2001
样例输出 #1
3
1992 1996 2000

提示

数据保证,1582x<y3000

参考代码

cpp
#include <bits/stdc++.h>

using namespace std;

bool leap(int y) {
	if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) return true;
	return false;
}

int main() {
	int x, y; cin >> x >> y;

	int cnt = 0;
	for (int i = x; i <= y; i++) {
		if (leap(i)) cnt++;
	}
	cout << cnt << endl;

	for (int i = x; i <= y; i++) {
		if (leap(i)) cout << i << " ";
	}
	cout << endl;

	return 0;
}

5.2 质数筛

题目描述

输入 n 个不大于 105 的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。

输入格式

第一行输入一个正整数 n,表示整数个数。

第二行输入 n 个正整数 ai,以空格隔开。

输出格式

输出一行,依次输出 ai 中剩余的质数,以空格隔开。

样例 #1

样例输入 #1
5
3 4 5 6 7
样例输出 #1
3 5 7

提示

数据保证,1n1001ai105

参考代码

cpp
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int a[N];

void prime() {
	a[0] = a[1] = 1;

	int t = sqrt(N);
	for (int i = 2; i <= t; i++) {
		if (!a[i]) {
			for (int j = i; i * j <= N; j++) {
				a[i * j] = 1;
			}
		}
	}
}

int main() {
	prime();

	int n; cin >> n;
	for (int i = 1; i <= n; i++) {
		int t; cin >> t;
		if (!a[t]) cout << t << " ";
	}
	cout << endl;

	return 0;
}

5.3 素数个数

题目描述

编程求 2n(n 为大于 2 的正整数)中有多少个素数。

输入格式

输入 n(2n50000)

输出格式

素数个数。

样例 #1

样例输入 #1
10
样例输出 #1
4

参考代码

cpp
#include <bits/stdc++.h>

using namespace std;

bool is_prime(int n) {
	int t = sqrt(n);
	for (int i = 2; i <= t; i++) {
		if (n % i == 0) return false;
	}
	return true;
}

int main() {
	int n; cin >> n;

	int cnt = 0;
	for (int i = 2; i <= n; i++) {
		if (is_prime(i)) cnt++;
	}
	cout << cnt << endl;

	return 0;
}

5.4 约分

题目描述

输入一个分数的分子和分母,形如:ab,输出它的最简分数。

输入格式

两个整数 ab 表示一个分数

输出格式

ab 的最简形式 x/y

样例 #1

样例输入 #1
6 12
样例输出 #1
1/2

参考代码

cpp
#include <bits/stdc++.h>

using namespace std;

int gcd(int n, int m) {
	return m ? gcd(m, n % m) : n;
}

int main() {
	int a, b; cin >> a >> b;

	int g = gcd(a, b);
	cout << a / g << "/" << b / g << endl;

	return 0;
}

附录 A

1 一维数组作为参数传递

cpp
void print_array(int a[], int len) {
	for (int i = 0; i < len; i++) {
		cout << a[i] << " ";
	}
}

int main() {
	int a[5] = {0, 1, 2, 3, 4};
	print_array(a, 5); // 传址调用

	return 0;
}

2 二维数组作为参数传递

cpp
void print_array(int a[][5], int row, int col) {
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			cout << a[i][j] << " ";
		}
	}
}

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

	return 0;
}

3 lambda 函数

C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。

Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。

Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下:

[capture](parameters)->return-type{body}

例如:

[](int x, int y){ return x < y ; }

如果没有返回值可以表示为:

[capture](parameters){body}

例如:

[]{ ++global_x; }

在一个更为复杂的例子中,返回类型可以被明确的指定如下:

[](int x, int y) -> int { int z = x + y; return z + x; }

本例中,一个临时的参数 z 被创建用来存储中间结果。如同一般的函数,z 的值不会保留到下一次该不具名函数再次被调用时。

如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。

在 Lambda 表达式内可以访问当前作用域的变量,这是 Lambda 表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:

[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

另外有一点需要注意。对于[=][&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:

[this]() { this->someFunc(); }();