函数
函数的声明
-
函数的声明是为了告诉编译器如何调用这个函数,形式如下:
返回值类型 函数名(参数1类型 参数1, 参数2类型 参数2, ..., 参数n类型 参数n);
- 返回值类型:一个函数可以返回一个值,返回值类型是函数返回的值的数据类型。
- 有些函数执行所需的操作而不返回任何值,此时的返回值类型是
void
- 有些函数执行所需的操作而不返回任何值,此时的返回值类型是
- 函数名:这是函数的实际名称
- 一般会按照函数功能取名
- 参数:声明时的参数称为形式参数,类似于占位符
- 参数是可选的,函数可能不包含参数,也可以包含很多个参数
- 在函数被调用之前,我们并不知道形式参数的值是什么
- 返回值类型:一个函数可以返回一个值,返回值类型是函数返回的值的数据类型。
函数的定义
- C++中定义函数时,需要在声明的基础上写好函数体,函数定义的形式如下:
返回值类型 函数名(参数1类型 参数1, 参数2类型 参数2, ..., 参数n类型 参数n) {
函数体
}
函数的调用
- C++中,定义了函数之后,函数里面的代码是不会执行的,只有调用了这个函数,才会去执行函数中的代码。
- 当函数被调用时,我们向参数传递一个值,这个值被称为**实际参数*。
Tips:如果我们函数的定义在调用前进行的话,也可以省略函数的声明这一步。
举例:调用MyMax
函数,返回两个整型数中更大的一个数。
#include <iostream>
using namespace std;
// 定义这个函数,在调用前就定义了,所以可以省略声明
int MyMax(int a, int b) {
int max_num;
if (a > b)
max_num = a;
else
max_num = b;
return max_num;
}
int main() {
int num1 = 30;
int num2 = 50;
// 变量 num3 表示这两个数字中更大的值
int num3;
// 调用
num3 = MyMax(num1, num2);
cout << num3 << endl;
return 0;
}
以上代码以main()
函数为入口开始执行,运行到 MyMax(num1, num2)
时会调用MyMax
函数。过程如下:
num1
和num2
作为实际参数传递给MyMax
函数- 其中,
num1
(30)传给形参a
,num2
(50)传给形参b
- 其中,
- 进入到
MyMax
函数中执行代码- 变量
max_num
是在函数体中声明的,称为局部变量,只有在这个函数里能访问到这个变量 Tips:如果把max_num
声明在函数外,就是一个全局变量,所有函数,包括main()
函数都可以访问。
- 变量
- 返回一个整型数
max_num
(50),并离开MyMax
函数回到main()
函数MyMax(num1, num2)
的计算结果为50,返回的结果将被赋值给num3
参数传递: 传值和引用
调用函数时,参数的传递一般分为传值传递和引用传递
- 传值传递
- 把参数的实际值赋值给函数的形式参数
- 修改函数内的形式参数对实际参数没有影响
- 引用传递
- 该方法把参数的引用赋值给形式参数
- 函数内通过会引用访问实际参数,若进行修改会改变实际参数的值
#include <iostream>
using namespace std;
// 传值传递
void swap1(int x, int y) {
cout << "传值传递:" << endl;
cout << "交换前,x 的值:" << x << endl;
cout << "交换前,y 的值:" << y << endl;
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
cout << "交换后,x 的值:" << x << endl;
cout << "交换后,y 的值:" << y << endl;
return;
}
// 引用传递
void swap2(int &x, int &y) {
cout << "引用传递:" << endl;
cout << "交换前,x 的值:" << x << endl;
cout << "交换前,y 的值:" << y << endl;
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把地址 y 的值赋值给地址 x */
y = temp; /* 把地址 x 的值赋值给地址 y */
cout << "交换后,x 的值:" << x << endl;
cout << "交换后,y 的值:" << y << endl;
return;
}
int main () {
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
// 调用传值传递的函数来交换值
swap1(a, b);
cout << "传值传递交换后,a 的值:" << a << endl;
cout << "传值传递交换后,b 的值:" << b << endl;
// 调用引用传递的函数来交换值
swap2(a, b);
cout << "引用传递交换后,a 的值:" << a << endl;
cout << "引用传递交换后,b 的值:" << b << endl;
return 0;
}
//交换前,a 的值:100
//交换前,b 的值:200
//传值传递:
//交换前,x 的值:100
//交换前,y 的值:200
//交换后,x 的值:200
//交换后,y 的值:100
//传值传递交换后,a 的值:100
//传值传递交换后,b 的值:200
//引用传递:
//交换前,x 的值:100
//交换前,y 的值:200
//交换后,x 的值:200
//交换后,y 的值:100
//引用传递交换后,a 的值:200
//引用传递交换后,b 的值:100
参数传递: 数组参数
- 数组名表示的是数组的地址,所以这时候我们传递的参数实际上是数组的地址。
- 在函数中修改数组里的某一个值,会在内存中直接修改这个数组。
#include <iostream>
using namespace std;
// TODO 补充第一个参数:整型数组a
int MyMax(int a[], int len) {
int max_num = 0;
for (int i=0; i<len; i++) {
if (a[i] > max_num)
max_num = a[i];
}
return max_num;
}
int main() {
int nums[10] = {3, 2, 1, 1, 2, 3, 3, 2, 1, 0};
cout << MyMax(nums, 10) << endl;
return 0;
}
//3
参数传递:参数默认值
- 函数定义时,我们可以给函数的参数设置默认值
- 这样我们调用函数时,可以选择不传递参数,选择默认的参数。
举例:比较两个数大小返回最大值的函数,我们可以把形式参数b
的默认值设置为100。
设置参数默认值的程序段
// 定义这个函数,在调用前就定义了,所以可以省略声明
int MyMax(int a, int b = 100) {
int max_num;
if (a>b)
max_num = a;
else
max_num = b;
return max_num;
}
- 在调用函数时,如果只传递一个参数
a
,比如MyMax(30)
,函数就会默认b
的值为100,效果等同于MyMax(30, 100)
。 - 如果调用函数时,还是传递了两个参数,比如
MyMax(30, 80)
,那么就不使用b
的默认值。
Tips:若给某一参数设置了默认值,那么在参数列表中,位于该参数之后的所有参数都必须也设置默认值。
递归函数
递归函数的两个基本要素
-
递归函数的写法有两个基本要素:
-
递归关系
- 问题规模为n时与问题规模为n-1时之间的转换关系
- 计算某个数字的阶乘,递归的关系就是:数字n的阶乘 = 数字n * 数字n-1的阶乘
- 这个函数可以写成: 递归关系的程序段
int factorial(int n) { return n * factorial(n-1); }
Tips:只知道递归关系是不够的,比如上面这个函数,就会无休止的一直套娃套下去,没有尽头。
-
递归的终止条件
- 递归关系需要有一个递归终止的条件。
- 在计算阶乘中,递归终止的条件是计算到数字1的阶乘的时候,函数可以直接返回数字1的阶乘就是1
- 补充上这个条件: 递归终止条件的程序段
int factorial(int n) { if (n == 1) return 1; else return n * factorial(n-1); }
Tips:更严谨的,我们可以在
n
小于1的时候,输出错误操作。
-
🌰
汉诺塔
左到右有A、B、C三根柱子,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间需要遵循以下原则:
一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤。若将A最上面的盘子移动到B上,则可表示为 "A -> B"。
输入描述:
一行,一个整数n (n <= 20)。
输出描述:
若干行,每行代表一次操作。
比如:
A -> B
示例 1:
输入:
2
输出:
A -> B
A -> C
B -> C
#include <bits/stdc++.h>
using namespace std;
void hanoi(int N, char source, char relay, char destination){
if(N==1)
cout << source << " -> " << destination << endl;
else{
hanoi(N-1, source, destination, relay);
cout << source << " -> " << destination << endl;
hanoi(N-1,relay,source,destination);
}
}
int main() {
// 请补全代码,实现题目功能
int n;
cin >> n;
hanoi(n,'A','B','C');
return 0;
}
结构体
结构体的定义
- 结构体有很多个不同类型的变量,用于表示这个结构体的属性,称之为结构体的成员变量。
- 在使用结构体之前,我们需要在代码中定义结构体,定义方式如下:
struct 结构体名称 {
数据类型1 变量名1;
数据类型2 变量名2, 变量名3;
...
数据类型n 变量名m;
};
定义结构体时需要注意:
- 使用
struct
关键字 - 每个结构体都有自己的名称
- 例如储存学生信息的结构体可以命名为
Student
。
- 例如储存学生信息的结构体可以命名为
- 在结构体内部声明成员变量
- 例如学生的姓名、学号、出生年份等信息
- 注意大括号后面需要有分号
举例:定义学生信息的结构体:
学生信息结构体的代码段
struct Student{
int number, birth_year;
string name;
};
结构体变量声明
- 定义结构体之后,我们就可以声明结构体类型的变量,作为结构体的实例。
Tips:声明结构体变量的方式和声明普通变量是一样的,在声明结构体变量的时候,系统就会为它们在内存上分配空间了。
举例:声明Student
结构体的实例
- 声明结构体变量,每个变量都包含具有相同名称的成员变量。
声明结构体变量的程序段
struct Student{
int number, birth_year;
string name;
};
// 声明三个学生
Student zhang_san, li_si, wang_mazi;
- 另外,我们也可以声明结构体变量的数组,以保存500个学生的信息。
声明结构体变量数组的程序段
// 声明一个学生数组,数组长度为500,可以保存500个学生的信息
Student students[500];
另一种声明结构体变量的方式是:直接在定义结构体时,声明结构体变量。
定义时声明结构体变量的程序段
struct Student{
int number, birth_year;
string name;
} zhang_san, li_si, wang_mazi;
struct Student{
int number, birth_year;
string name;
} students[500];
初始化结构体
- 对于结构体变量,可以通过两种方式初始化它:使用初始化列表或构造函数。
- 使用初始化列表和数组的初始化类似,会根据给出的元素依次初始化结构体中的每个成员
- 如果给出的元素小于成员数量,排在后面的就会保持没有被初始化
使用初始化列表的程序段
struct Student{
int number, birth_year;
string name;
};
// 初始化一个学号为1,2000年出生,名叫 ZhangSan的学生
Student zhang_san = {1, 2000, "ZhangSan"};
Tips:使用初始化列表会有一些问题:比如,如果有某个成员未被初始化,那么在这种情况下,跟随在该成员后面的成员都不能初始化。
- 采用构造函数初始化,可以在创建一个结构体变量而不向其传递某些成员变量的值时,提供默认值
- 可以先在结构体内部完成一个构造函数,再调用这个构造函数来初始化结构体变量
结构体成员访问
- 要访问某一个结构体变量的某个成员,可以使用
结构体变量名.结构体成员变量名
的方式访问
结构体作为函数参数
结构体变量也可以通过传值传递和引用传递给函数。
- 传值传递意味着需要生成整个原始结构的副本并传递给函数。
Tips:因为不希望浪费时间和空间来复制整个结构体,所以传值传递一般在结构很小时采用。
- 引用传递意味着函数可以访问原始结构的成员变量,从而可能更改它们
- 如果不想让函数更改任何成员变量值,那么可以将结构体变量作为一个常量引用传递给函数
- 常量引用传递需要在参数类型前加上
const
关键字。
举例:展示学生信息
展示学生信息的程序段
// 定义函数
void showStudent(const Student &student_info) {
cout << "Student Number : " << student_info.number << endl;
cout << "Name : " << student_info.name << endl;
cout << "Birth Year : " << student_info.birth_year << endl;
}
// 调用函数
showStudent(zhang_san);
🌰
年龄最大学员
输入n个学生的信息,包括姓名、性别、年龄,再输出其中年龄最大的学生的信息。(保证最大年龄不重复)
1 <= n <= 10
姓名长度小于等于20
性别为M或F
输入描述:
第一行一个整数n
接下来n行,依次是学生的姓名、性别、年龄。
输出描述:
一行,依次是姓名、性别、年龄,中间用空格隔开。
示例 1:
输入:
2
Kal'tsit F 1000
Amiya F 14
输出:
Kal'tsit F 1000
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
char gender;
int age;
};
int main() {
int num, max_age, max_age_idx;
cin >> num;
string n;
char g;
int a;
Student students[11];
for (int i=0; i<num; i++) {
cin >> n >> g >> a;
students[i] = {n, g, a};
}
max_age = 0;
max_age_idx = 0;
for (int i=0; i<num; i++) {
if (students[i].age > max_age) {
max_age = students[i].age;
max_age_idx = i;
}
}
cout << students[max_age_idx].name << " " << students[max_age_idx].gender << " " << students[max_age_idx].age;
return 0;
}