函数具有一定功能的独立的代码模块。每个C++程序都至少有一个函数!即main函数。常用的头文件中为我们封装了一些函数,如cmath中的sqrt、abs等,使用它们使我们的编码变得轻松愉快。当然,我们也可以自定义函数,以实现特定的功能需求。
1 函数的声明和定义
1.1 函数的定义
C++中函数定义的一般形式如下:
return_type function_name(parameter_list) {
body of the function
}在C++中,函数由函数头和函数体(执行特定任务的语句块)组成,函数头包括:
- 返回值类型:函数返回值的类型,如果什么都不返回类型为
void。 - 函数名称:函数名称和参数列表构成了函数签名。
- 参数列表:调用函数时向函数传递的参数(实参),函数参数分为实参和形参。参数列表可以为空。
示例
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);如上例中的函数声明是:
// 1
int max(int a, int b);
// 或者写成
int max(int, int);
// 2
void print();- 在函数声明中,参数的名称并不重要,只有参数的类型是必须的。
函数声明是必须的吗?我应该什么时候写函数声明?
函数声明不是必须的。如果函数定义在调用函数之前则不必写函数声明,如果函数定义在调用函数之后,为了告诉编译器调用函数的相关信息,我们需要在调用函数时给出函数声明,这时函数定义可以写在任何地方。
2 调用函数
程序通过函数名来调用函数。当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数执行结束(遇到return语句或者是函数结束的右花括号)会把程序的控制权交还给主程序(main)。
函数调用时如果需要传递参数,这时传递的值被称为实参(函数定义时的参数被称为形参)。实参和形参需要严格对应:个数、顺序、类型。
示例:
#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++有两种种参数传递方式
- 传值调用:该方法把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
- 传址调用:该方法把参数的地址或引用赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
2.1.1 传值调用
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 传址调用
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;这里的b和a是一样的,都指向同一个内存单元,修改b,a也会跟着变。可以把b看作a的别名。
3 函数的默认参数
当您定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。
这是通过在函数定义中使用赋值运算符来为参数赋值的。调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值。请看下面的实例:
#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 距离函数
题目描述
给出平面坐标上不在一条直线上三个点坐标
对于平面上的两个点
输入格式
输入三行,第
输出格式
输出一个两位小数,表示由这三个坐标围成的三角形的周长。
样例 #1
样例输入 #1
0 0
0 3
4 0样例输出 #1
12.00提示
数据保证,坐标均为实数且绝对值不超过
#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 歌唱比赛
题目描述
输入格式
第一行两个整数
接下来
输出格式
输出分数最高的同学的分数,保留两位小数。
样例 #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#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 计算阶乘
题目描述
求
挑战:尝试不使用循环语句(for、while)完成这个任务。
输入格式
第一行输入一个正整数
输出格式
输出一个正整数,表示
样例 #1
样例输入 #1
3样例输出 #1
6提示
数据保证,
#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 递归拓展-赦免战俘
- 有点问题
#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 闰年展示
题目描述
输入
输入格式
输入两个正整数
输出格式
第一行输出一个正整数,表示
第二行输出若干个正整数,按照年份单调递增的顺序输出所有闰年年份数字。
样例 #1
样例输入 #1
1989 2001样例输出 #1
3
1992 1996 2000提示
数据保证,
参考代码
#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 质数筛
题目描述
输入
输入格式
第一行输入一个正整数
第二行输入
输出格式
输出一行,依次输出
样例 #1
样例输入 #1
5
3 4 5 6 7样例输出 #1
3 5 7提示
数据保证,
参考代码
#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 素数个数
题目描述
编程求
输入格式
输入
输出格式
素数个数。
样例 #1
样例输入 #1
10样例输出 #1
4参考代码
#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 约分
题目描述
输入一个分数的分子和分母,形如:
输入格式
两个整数
输出格式
x/y
样例 #1
样例输入 #1
6 12样例输出 #1
1/2参考代码
#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 一维数组作为参数传递
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 二维数组作为参数传递
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(); }();
