函数
函数的声明
-  函数的声明是为了告诉编译器如何调用这个函数,形式如下: 返回值类型 函数名(参数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;
}










