1. C++面向过程

b站黑马程序员C++课程学习笔记,图片存在github,可能需要翻墙才可见

一、C++简介

1.1 C++的产生及其特点

  • 从C语言发展演变而来,解决了C语言中存在的一些问题,并增加了对面向对象程序设计方法的支持
    • 与其他高级语言相比,C语言可以直接访问物理地址;与汇编相比它具有良好的可读性和可移植性
  • C++于1980年由贝尔实验室的Bjarne Stroustrup创建
  • 特点
    • 尽量兼容C语言
    • 支持面向对象的方法
      • 对象是程序的基本单元
      • 对象的属性需要用某种类型的数据来表示
      • 对象的功能和行为成员函数来实现,函数的实现归根到底是算法的设计

1.2 Hello World

// #include头文件,既用于声明头文件以供多个文件使用,同时也用来声明函数原型,以便于在整个程序中使用
#include <iostream>

// 主函数,程序入口
int main(){
    std::cout << "Hello World !" << std::endl;
    std::cout << "Welcome to C++ ! " << std::endl;
    return 0;
}

二、C++语法基础

2.1 标识符命名规则

  • 字母或者下划线开始
  • 可以由大写字母、小写字母、下划线或数字0~9组成
  • 区分大小写
  • 不能是C++关键字或操作符

2.2 注释

  1. 单行注释://注释描述
  2. 多行注释:/* 注释描述 */

2.3 变量

  • 作用:给一段指定的内存空间命名,方便操作这段内存

  • 语法:数据类型 变量名 = 初始值;

    #include<iostream>
    using namespace std;
    
    int main() {
    
    	//变量的定义
    	//语法:数据类型  变量名 = 初始值
    
    	int a = 10;
    	cout << "a = " << a << endl;
    	
    	system("pause");
    	return 0;
    }
    

2.4 常量

  • 作用:记录程序中不可更改的数据

  • 定义方式:

    • #define 宏常量#define 常量名 常量值
      • 没有数据类型,只是简单的字符串替换,不能进行安全检查
    • const常量const 数据类型 常量名 = 常量值
      • 常类型,创建时必须初始化
      • 编译期概念,即编译时用到的地方都会替换成相应的值,可节省空间,避免不必要的内存分配
      • 作用:
        • 防止修改,起保护作用,增加程序的健壮性
        • 可以节省空间,避免不必要的内存分配
    //1、宏常量
    #define day 7
    
    int main() {
    	cout << "一周里总共有 " << day << " 天" << endl;
    	//day = 8;  //报错,宏常量不可以修改
    
    	//2、const修饰变量
    	const int month = 12;
    	cout << "一年里总共有 " << month << " 个月份" << endl;
    	//month = 24; //报错,常量是不可以修改的
    	
    	system("pause");
    	return 0;
    }
    
  • const对象默认为文件局部变量

    • 默认情况下,const对象被设定为仅在文件内有效,如果要在不同的程序文件中使用同一个const对象,则必须要显示声明extern,并初始化
    • 多个文件之间共享const对象,那么不管是声明还是定义都可添加上extern关键字,这样就只需要定义一次即可
    • 非const变量默认为extern,因此不需要显示声明
    // file_1.cpp
    // 定义并初始化了一个常量,该常量可以被其他文件访问
    extern const int bufSize = fcn();
    
    // file_2.h
    // 与file_1.cpp中定义的bufSize是同一个
    extern const int bufSize;
    
    // file_1.cpp定义并初始化了bufSize,因此显然是一个定义,加上了extern则可以被其他文件访问
    // file_2.h头文件中的声明也由extern做了限定,其作用是指明bufSize并非本文独有,它的定义将在别处出现
    

2.5 数据类型

  • 作用:
    • 存储所需要的尺寸
    • 取值空间
    • 对齐信息
    • 可执行的操作
  • 不同变量类型之间的唯一区别就是内存空间大小
    • 1 byte = 8 bit
    • 1 bit 可为0或1
    • 有符号数signed,需要有一位表示符号+/- ;无符号数unsigned
2.5.1 整型 int
image-20221013105420775
  1. sizeof关键字
    1. 作用:统计数据类型所占内存大小
    2. 语法:sizeof( 数据类型 / 变量)
    3. 例如:sizeof(float)
    4. 整型内存大小的一个结论:short < int <= long <= long long
2.5.2 浮点型(实型)
  1. 作用:表示小数

  2. 分成两种:floatdouble

image-20221013105547697
  1. 默认情况下,输出一个小数,会显示出6位有效数字
2.5.3 字符型 char
  1. 作用:用于显示单个字符

  2. 语法:char ch = 'a';

  3. 注意:

    1. 用单引号将字符括起
    2. 单引号内只能有一个字符
  4. C/C++中字符型变量只占用一个字节

  5. 不是把字符本身放到内存中存储,而是将对应的ASCII编码放入存储单元

    int main() {
    	char ch = 'a';
    	cout << ch << endl;
    	cout << sizeof(char) << endl;
    
    	//ch = "abcde"; //错误,不可以用双引号
    	//ch = 'abcde'; //错误,单引号内只能引用一个字符
    
    	cout << (int)ch << endl;  //查看字符a对应的ASCII码
    	ch = 97; //可以直接用ASCII给字符型变量赋值
    	cout << ch << endl;
    
    	system("pause");
    	return 0;
    }
    
  6. 转义字符:用于表示一些不能显示出来的ASCII字符

    image-20221013105720841
  7. 字符串型

    • C风格:char 变量名[]="字符串值"

      • 要加中括号
      int main() {
      	char str1[] = "hello world";
      	cout << str1 << endl;
          
      	system("pause");
      	return 0;
      }
      
    • C++风格:string 变量名="字符串值"

      • 要包含头文件:#include <string>
      #include <string>
      
      int main() {
      	string str = "hello world";
      	cout << str << endl;
      	
      	system("pause");
      	return 0;
      }
      
2.5.4 布尔型 bool
  1. 只有两个值

    • true:本质是1,非0都是1
    • false:本质是0
  2. 只占1个字节

    int main() {
    
    	bool flag = true;
    	cout << flag << endl; // 1
    
    	flag = false;
    	cout << flag << endl; // 0
    
    	cout << "size of bool = " << sizeof(bool) << endl; //1
    	
    	system("pause");
    	return 0;
    }
    

2.6 标准库类型string

标准库 string 表示可变长的字符序列

使用string类型时必须要首先包含string头文件

#include <string>
  1. 定义和初始化

    string s1;		// 默认初始化,s1是一个空字符串
    
    string s2(s1);	// 直接初始化
    
    string s2 = s1;	// 拷贝初始化
    
    • 如果使用等号=初始化一个变量,实际上就是执行的拷贝初始化
    • 不使用等号=,则执行的直接初始化
    string s3(n, 'c');	// 把s3初始化为由连续n个字符c组成的串
    
    // 例如:
    string s3(5, 'c');	// s3的初始化内容是5个c组成的串:ccccc
    
  2. string对象的操作

    • 执行读取操作时,string对象会自动忽略开头的空白,从第一个真正的字符开始读起
    • 输入:“ HelloWorld ”,输出:“HelloWorld”
    #include <string>
    
    int main() {
        string s;
        std::cin >> s;
        std::cout << s << std::endl;
        return 0;
    }
    
    • 多个输入或多个输出可以连写在一起
    string s1, s2;
    cin >> s1 >> s2;			// 第一个输入读到s1中, 第二个输入读到s2中
    cout << s1 << s2 << endl;
    
  3. getline

    • 读取一整行
    • 可保留字符串中的空白符
    • getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,将读入内存的存放到string对象中
    • getline遇到换行符就结束读取操作并返回结果
    int main() {
        string line;
        
        // 每次循环都是读入一整行,
        while (getline(cin, line)) {
            cout << line << endl;
        }
        return 0;
    }
    
  4. empty

    • string的成员函数
    • 根据string对象是否为空返回一个对应的布尔值
    while ( getline(cin, line) ) {
        if ( !line.empty() ) {
            count << line << endl;
        }
    }
    
  5. size

    • string的成员函数
    • 返回string对象的长度,即string对象中字符的个数
    string line;
    
    while (getline(cin, line)) {
        if (line.size() > 80)
            cout << line << endl;
    }
    
    • size函数返回的是一个string::size_type类型的值

2.7 标准库类型vector

  • vector表示对象的集合,其中所有对象的类型都相同,集合中的每个对象都有一个与之对应的索引,该索引可用于访问对象

  • vector容纳着其他对象,因此也常称为容器(container)

  • 使用vector必须包含vector头文件

    #include <vector>
    
    using std::vector;
    
  • vector是一个类模板

    • 模板本身不是类或函数,可将其看作编译器生成类或函数编写的一份说明
    • 当使用模板时,需要指出编译器应该把类或函数实例化成何种类型
    • 实例化:编译器根据模板创建类或函数的过程
  1. 定义和初始化

    vector<T> v1;					// T是对象类型,v1是一个空的vector对象,执行默认初始化
    
    vector<T> v2(v1);				// v2中包含v1所有元素的副本
    vector<T> v2 = v1;				// 同上
    
    vector<T> v3(n, val);			// v3包含n个重复的元素, 每个元素值都是val
    
    // 值初始化
    vector<T> v4(n)				   // 只提供容纳的元素数量,初值由元素类型决定,如int型则初值为0
    
    // 列表初始化
    vector<T> v4{a, b, c, ...};  	// v4包含了初始值个数的元素,每个元素被赋予相应的初始值
    vector<T> v5 = {a, b, c, ...};	// 同上
    
    
  2. push_back

    • vector的成员函数,用于添加元素
    • 将一个值作为vector对象的尾元素push到vector对象的尾端
    // 将0 ~ 99的整数存储到v2中
    
    vector<int> v2;
    
    for (int i = 0; i != 100; ++i) {
        v2.push_back(i);
    }
    
    // 从标准输入中读取单词,将其作为vector对象的元素存储
    
    string word;
    vector<string> text;
    
    while ( cin >> word ) {
        text.push_back(word);
    }
    
    • 如果循环体内包含有向vector对象添加元素的语句,则不能使用范围for循环
      • 范围for循环语句体内不应改变其所遍历序列的大小

注意:

  • 不能用下标形式给vector对象添加元素

  • 可用于访问已存在的元素,下标从0开始

2.6 数据输入

  • 从键盘获取输入数据

    int main(){
    
    	//整型输入
    	int a = 0;
    	cout << "请输入整型变量:" << endl;
    	cin >> a;
    	cout << a << endl;
    
    	//浮点型输入
    	double d = 0;
    	cout << "请输入浮点型变量:" << endl;
    	cin >> d;
    	cout << d << endl;
    
    	//字符型输入
    	char ch = 0;
    	cout << "请输入字符型变量:" << endl;
    	cin >> ch;
    	cout << ch << endl;
    
    	//字符串型输入
    	string str;
    	cout << "请输入字符串型变量:" << endl;
    	cin >> str;
    	cout << str << endl;
    
    	//布尔类型输入
    	bool flag = true;
    	cout << "请输入布尔型变量:" << endl;
    	cin >> flag;
    	cout << flag << endl;
    	system("pause");
    	return EXIT_SUCCESS;
    }
    

2.7 类型别名

两种方式:

  1. typedef

    typedef double d;	// d是类型double的同义词
    
  2. 别名声明:using

    using d = double;	// 把等号左侧的名字规定成右侧的类型的别名
    
2.7.1 auto
  • 让编译器通过初始值来推算变量的类型

  • auto定义的变量必须要有初始值

    auto a = val1 + val2;
    
    // a初始化为val1和val2相加的结果
    
  • auto可以在一条语句中声明多个变量,但必须注意一条声明语句只能有一个基本数据类型

    auto i = 1, *p = &i;	// i是整型变量,p是整型指针
    
    auto a = 0, b = 3.14;	// 错误!!!
    
2.7.2 decltype
  • 选择并返回操作数的数据类型

    decltype( fcn() ) sum = x;	// sum的类型是函数fcn返回类型
    
  • 编译器会分析表达式并得到它的类型,不会实际计算表达式的值

三、运算符

  • 用于执行代码运算
image-20221013111034152

3.1 算术运算符

  • 处理四则运算
image-20221013111113926
  1. 加减乘除

    int main() {
    	int a1 = 10;
    	int b1 = 3;
    	cout << a1 + b1 << endl;
    	cout << a1 - b1 << endl;
    	cout << a1 * b1 << endl;
    	cout << a1 / b1 << endl;  //两个整数相除结果依然是整数
    
    	int a2 = 10;
    	int b2 = 20;
    	cout << a2 / b2 << endl; 
    
    	int a3 = 10;
    	int b3 = 0;
    	//cout << a3 / b3 << endl; //报错,除数不可以为0
    
    	//两个小数可以相除
    	double d1 = 0.5;
    	double d2 = 0.25;
    	cout << d1 / d2 << endl;
    
    	return 0;
    }
    
  2. 取模

    int main() {
    	int a1 = 10;
    	int b1 = 3;
    	cout << 10 % 3 << endl;
    
    	int a2 = 10;
    	int b2 = 20;
    	cout << a2 % b2 << endl;
    
    	int a3 = 10;
    	int b3 = 0;
    	//cout << a3 % b3 << endl; //取模运算时,除数也不能为0
    
    	//两个小数不可以取模
    	double d1 = 3.14;
    	double d2 = 1.1;
    	//cout << d1 % d2 << endl;
    
    	return 0;
    }
    
  3. 递增递减

    int main() {
    	//后置递增
    	int a = 10;
    	a++; //等价于a = a + 1
    	cout << a << endl; // 11
    
    	//前置递增
    	int b = 10;
    	++b;
    	cout << b << endl; // 11
    
    	//区别
    	//前置递增先对变量进行++,再计算表达式
    	int a2 = 10;
    	int b2 = ++a2 * 10;
    	cout << b2 << endl;
    
    	//后置递增先计算表达式,后对变量进行++
    	int a3 = 10;
    	int b3 = a3++ * 10;
    	cout << b3 << endl;
    
    	return 0;
    }
    

3.2 赋值运算符

image-20221013111543733

3.3 比较运算符

  • 用于表达式的比较,并返回一个真值或假值
image-20221013111642016

3.4 逻辑运算符

  • 用于根据表达式的值返回真值或假值
image-20221013111726085

四、基本控制结构

C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构

  • 顺序结构:程序按顺序执行,不发生跳转
  • 选择结构:依据条件是否满足,有选择的执行相应功能
  • 循环结构:依据条件是否满足,循环多次执行某段代码

4.1 选择结构

4.1.1 if语句
  1. 单行格式 if 语句

    int main() {
    	//选择结构-单行if语句
    	//输入一个分数,如果分数大于600分,视为考上一本大学,并在屏幕上打印
    	int score = 0;
    	cout << "请输入一个分数:" << endl;
    	cin >> score;
    	cout << "您输入的分数为: " << score << endl;
    
    	//if语句
    	//注意事项,在if判断语句后面,不要加分号
    	if (score > 600) {
    		cout << "我考上了一本大学!!!" << endl;
    	}
        
    	return 0;
    }
    
  2. 多行格式 if 语句

    int main() {
    	int score = 0;
    	cout << "请输入考试分数:" << endl;
    	cin >> score;
    
    	if (score > 600) {
    		cout << "我考上了一本大学" << endl;
    	}
    	else {
    		cout << "我未考上一本大学" << endl;
    	}
    
    	return 0;
    }
    
  3. 多条件的 if 语句

    int main() {
    	int score = 0;
    	cout << "请输入考试分数:" << endl;
    	cin >> score;
    
    	if (score > 600) {
    		cout << "我考上了一本大学" << endl;
    	}
    	else if (score > 500) {
    		cout << "我考上了二本大学" << endl;
    	}
    	else if (score > 400) {
    		cout << "我考上了三本大学" << endl;
    	}
    	else {
    		cout << "我未考上本科" << endl;
    	}
    
        return 0;
    }
    
    /**********************等价于*************************/
    else {
        if (score > 500) {
    		cout << "我考上了二本大学" << endl;
    	}
    }
    
  4. 嵌套if语句

    // 案例需求:
    // 提示用户输入一个高考考试分数,根据分数做如下判断
    // 分数如果大于600分视为考上一本,大于500分考上二本,大于400考上三本,其余视为未考上本科
    // 在一本分数中,如果大于700分,考入北大,大于650分,考入清华,大于600考入人大
    int main() {
    	int score = 0;
    	cout << "请输入考试分数:" << endl;
    	cin >> score;
    
    	if (score > 600) {
    		cout << "我考上了一本大学" << endl;
    		if (score > 700) {
    			cout << "我考上了北大" << endl;
    		}
    		else if (score > 650) {
    			cout << "我考上了清华" << endl;
    		}
    		else {
    			cout << "我考上了人大" << endl;
    		}
    	}
    	else if (score > 500) {
    		cout << "我考上了二本大学" << endl;
    	}
    	else if (score > 400) {
    		cout << "我考上了三本大学" << endl;
    	}
    	else {
    		cout << "我未考上本科" << endl;
    	}
    
    	return 0;
    }
    
4.1.2 三目运算符
  • 语法:表达式1 ? 表达式2 :表达式3

    • 如果表达式1的值为真,执行表达式2,并返回表达式2的结果;
    • 如果表达式1的值为假,执行表达式3,并返回表达式3的结果。
    int main() {
    	int a = 10, b = 20, c = 0;
    	c = a > b ? a : b;
    	cout << "c = " << c << endl;
    
    	//C++中三目运算符返回的是变量,可以继续赋值
    	(a > b ? a : b) = 100;
    
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    	cout << "c = " << c << endl;
    
    	return 0;
    }
    
4.1.3 switch语句
  • 执行多条件分支语句

  • switch语句中表达式类型只能是整型或者字符型

  • case里如果没有break,那么程序会一直向下执行

    switch(表达式) {
    	case 结果1:执行语句; break;
    
    	case 结果2:执行语句; break;
    
    	...
    
    	default:执行语句;break;
    }
    
    //请给电影评分 
    //10 ~ 9   经典   
    // 8 ~ 7   非常好
    // 6 ~ 5   一般
    // 5分以下 烂片
    int main() {
    	int score = 0;
    	cout << "请给电影打分" << endl;
    	cin >> score;
    
    	switch (score) {
    	case 10:					// 没有break就会继续往下执行
    	case 9:
    		cout << "经典" << endl;
    		break;
    	case 8:
    		cout << "非常好" << endl;
    		break;
    	case 7:
    	case 6:
    		cout << "一般" << endl;
    		break;
    	default:
    		cout << "烂片" << endl;
    		break;
    	}
    	return 0;
    }
    

4.2 循环语句

4.2.1 while循环
  • 满足循环条件,执行循环语句

    int main() {
    	int num = 0;
    	while (num < 10) {
    		cout << "num = " << num << endl;
    		num++;
    	}
    	
    	return 0;
    }
    
4.2.2 do…while循环
  • 与while的区别在于do…while会先执行一次循环语句,再判断循环条件

    int main() {
    	int num = 0;
    
    	do {
    		cout << num << endl;
    		num++;
    	} while (num < 10);
    
    	return 0;
    }
    
4.2.3 for循环
  • 语法:for(起始表达式; 条件表达式; 末尾循环体) { 循环语句; }

    int main() {
    	for (int i = 0; i < 10; i++) {
    		cout << i << endl;
    	}
    
    	return 0;
    }
    
    /*****************等价于***********************/
    int i = 0;
    bool condition = true;
    for(;condition;) {
        i++;
        if(!(i < 10))
            condition = false;
    }
    
image-20221013113537826

4.3 break语句

  • 用于跳出选择结构或者循环结构

  • break使用的时机:

    • 出现在switch条件语句中,作用是终止case并跳出switch
    • 出现在循环语句中,作用是跳出当前的循环语句
    • 出现在嵌套循环中,跳出最近的内层循环语句
    int main() {
    	//2、在循环语句中用break
    	for (int i = 0; i < 10; i++) {
    		if (i == 5)	{
    			break; //跳出循环语句
    		}
    		cout << i << endl;
    	}
    
    	return 0;
    }
    

4.4 continue语句

  • 在循环语句中,跳过本次循环中余下尚未执行的语句继续执行下一次循环

    int main() {
    	for (int i = 0; i < 100; i++) {
    		if (i % 2 == 0) {
    			continue;
    		}
    		cout << i << endl;
    	}
    
    	return 0;
    }
    

五、指针

5.1 指针的基本概念

  • 指针是一个整数,存储一个内存地址

    • 电脑中的内存就像是一条大街,街边都是房子,每栋房子都有一个门牌号和地址,将每栋带地址的房子想象成一个字节的数据(1 byte)
      • 现在有人网购,需要送货上门;有人需要寄快递,把东西送出去。那么需要一个方法来获取正确的门牌号地址,也要能够从房子里搬东西出去,即能够从内存中,对那些byte进行read和write
      • 指针就是那个地址,告诉我们房子在什么地方 —— 指定的字节的在电脑内存的哪个位置
  • 语法:数据类型* 变量名;

    • 数据类型types对指针本身而言完全没有意义,因为所有指针都是一个整数,存放一个内存地址
      • 数据类型代表的是存放在这个内存中的数据的类型
    int main() {
    	//指针的定义:数据类型* 变量名 ;
    	int a = 10; 	//定义整型变量a
    	int* p = &a;	//定义并初始化指针, 指向变量a的地址
    
    	cout << &a << endl; //打印数据a的地址
    	cout << p << endl;  //打印指针变量p
    }
    

5.2 指针的使用

  • 在指针前面加一个星号*,即可访问指针指向内存中存储的数据,并对其进行操作

    • 星号*也称为解引用符
  • 这也称为Dereferencing(逆向引用,解引用)

    //通过*操作指针变量指向的内存
    cout << "*p = " << *p << endl;
    

5.3 指针与const

  1. 指向常量的指针(pointer to const,const在*左侧)

    const int *p = &a;
    // 或者
    int const *p = &a;
    
    • const修饰的是指针指向的对象类型,而不是指针本身

      const double pi = 3.14;		// pi是常量
      const double *p = &pi;		// p可以指向一个双精度常量
      
      double *p2 = &pi;	// 错误!!!p2是一个普通指针,不可以指向一个常量对象
      
    • 指向常量的指针可以指向一个非常量对象

      int a = 1;
      const int *p = &a;			// 不可以通过指针p来修改a的值
      
    • 常量指针指向可以修改(地址),但是指向的值不可以修改(地址保存的值)

  2. 常量指针(const pointer,const在*右侧)

    int *const p = &a;
    
    • const修饰的是指针本身

    • 常量指针必须要初始化,且该指针的值不能修改

    • 常量指针所指向的值可以通过非常量指针进行修改

      #include <iostream>
      using namespace std;
      
      int main() {
          int num = 0;
          int * const ptr = &num;  //const指针必须初始化!且const指针的值不能修改
          int * t = &num;			// 通过非常量指针对常量指针指向的值进行修改
          *t = 1;
          cout << *ptr << endl;
      }
      
  3. 顶层const

    • 顶层const(top-level const)表示指针本身是个常量
    • 底层const(low-level const)表示指针所指向的对象是个常量

六、引用

6.1 引用的基本概念和使用

  • 本质上只是指针的一个拓展

    • 没有reference能做而pointer不能做的事
  • 与指针的区别:

    • reference必须引用一个已存在的变量,引用本身并不是一个新的变量,不真正占用内存
  • 定义引用:数据类型& 引用名

    int main() {
    	int a = 10;
    	int &b = a; // b是a的别名
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
        
        // b是a的别名,修改b的值就是在修改a的值
    	b = 100;
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    
    	return 0;
    }
    
image-20221014160640224
  • 注意事项:

    • 引用必须初始化
    • 引用在初始化之后,不可以改变
    int main() {
    	int a = 10;
    	int b = 20;
        /***引用必须初始化***/
    	// int &c; 	   // 错误!!!
        
    	int &c = a;  	
        
        /***引用一旦初始化后,就不可以更改***/
    	// c = b; 		// 错误!!!
    
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    	cout << "c = " << c << endl;
    	return 0;
    }
    
image-20221014160856630

6.2 const引用

  • 将引用绑定到const对象上

  • 引用的类型必须与其所引用的对象的类型一致

  • 常量引用可以绑定到一个普通的类型对象上,只是不能通过该常量引用修改绑定对象的值

    const int a = 10;
    const int &b = a;
    
    int c = 5;
    const int &d = c;
    

七、函数

7.1 函数的定义与声明

  • 函数的组成:

    返回类型 函数名字(参数列表) {
        函数体
    }
    
  • 函数的声明(也称为函数接口):

    • 与定义的区别:函数声明不需要函数体
    • 函数接口三要素:
      • 返回类型
      • 函数名
      • 形参类型
  • 一般在头文件中进行声明,在源文件中定义

    • 含有函数声明的头文件应该被包含到定义函数的源文件中

7.2 参数传递

  • 每次调用函数都会重建它的形参,并用传入的实参对形参进行初始化
7.2.1 值传递
  • 实参的值被拷贝给形参,形参和实参是两个相互独立的对象
    • 实参被值传递,函数被传值调用
    • 函数对形参做的所有操作都不会影响实参
7.2.2 引用传递
  • 形参是引用类型,那么它将直接绑定到对应的实参上
    • 实参被引用传递,函数被传引用调用
image-20220604221128048 image-20220604221601919 image-20220604221754782 image-20220604222001271

7.3 函数分文件编写

  • 作用:让代码结构更加清晰
  • 步骤:
    • 创建后缀名为 .h 的头文件
      • 在头文件中写函数的声明
    • 创建后缀名为 .cpp 的源文件
      • 引用上一步的.h头文件
      • 在源文件中写函数的定义
    • 在主函数中调用函数,引用该函数的头文件

示例:

swap.h 头文件

# include <iostream>

//实现两个数字交换的函数声明
void swap(int a, int b);

swap.cpp 源文件

// 双引号""代表自定义的头文件
# include "swap.h"

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

main.cpp 主程序文件

# include <iostream>
# include "swap.h"

int main(){
    int a = 10;
    int b = 20;
    swap(a, b);
    
    return 0;
}

八、数组

  • 数组是一些元素的集合
    • 每个数据元素都是相同的数据类型
    • 连续的内存位置组成

8.1 一维数组

  • 定义方式

    int main(){
        // 方式1
        // 数据类型 数组名[元素个数];
        int score[10];
        
        // 利用下标赋值
        score[0] = 100;
        score[1] = 99;
        score[2] = 85;
        // 利用下标输出
        cout << score[0] << endl;
        cout << score[1] << endl;
        cout << score[2] << endl;
        
        // 方式2
        // 数据类型 数组名[元素个数] = {值1, 值2, 值3, ...};
        // 如果{}内不足10个数据,剩余数据用0补全
        int score2[10] = {100, 90, 80, 70, 60};
        
        // 逐个输出
        cout << score2[0] << endl;
        cout << score2[1] << endl; 
        // 逐个输出太麻烦,可利用循环进行输出
        for(int i = 0; i < 10; i++) {
            cout << score[i] << endl;
        }
        
        // 方式3
        // 数据类型 数组名[] = {值1, 值2, 值3, ...};
        int score3[] = {100, 90, 80, 70, 60};
    }
    
  • 数组名

    • 可统计整个数组在内存中的长度
    • 可获取数组在内存中的首地址
    int main()
    {
    	int arr[6] = { 11,22,33,44,55,66 };
    	cout << sizeof(arr) << endl;	//整个数组占用内存大小;
    	cout << sizeof(arr[0]) << endl;	//第一个元素占用内存大小;
        cout << sizeof(arr) / sizeof(arr[0]) << endl;	//数组中元素个数;
        
    	cout << (int)arr << endl;		//整个数组首地址,转十进制;
    	cout << (int)&arr[0] << endl;	//第一个元素的首地址,&是取址符,和整个数组相同;
    	cout << (int)&arr[1] << endl;	//第二个元素的首地址;
    	
        //数组名是常量,不可赋值;
        arr = 100; // 错误!!!arr是地址,不可以修改
    
    }
    
  • 案例

    #include <iostream>
    #include <string>
    #include <cmath>
    #include <ctime>
    using namespace std;
    
    int main() {
    	//五只小猪称体重
    	int arrpig[5] = { 300, 350, 200, 250, 400 };
        //设定一个最大值,访问数组中的每个值,如果比这个值大,就更新最大值;
    	int max = 0;
    	for (int i = 0; i < 5; i++)
    	{
    		//cout << arrpig[i] << endl;
    		if (arrpig[i] > max)
    		{
    			max = arrpig[i];	//访问数组中的每个值,如果比max大,就更新最大值;
    		}
    	}
    	cout << "最重的猪为" << max << "kg" << endl;
    
    
    
    	//数组元素逆置,原数组为1,2,3,4,5,逆置后为5,4,3,2,1;
    	int arrx[5] = { 1,2,3,4,5 };
    	cout << "逆置前的数组为:" ;
    	for (int n = 0; n < 5; n++)
    	{
    		cout << arrx[n];
    	}
    	printf("\n");
    
    	int start = 0;	//第一个元素下标;
    	int end = sizeof(arrx) / sizeof(arrx[0]) - 1;	//最后一个元素下标,算法为元素个数减1;
    	//只有当 start < end 时才互换;
    	while (start < end)
    	{
    		int temp = arrx[start];		// 定义一个临时储存空间,把第一个元素先放进去;
    		arrx[start] = arrx[end];	// 把最后一个元素放到第一个元素去;
    		arrx[end] = temp;		    // 把temp里的值放到最后一个元素去,实现互换;
    		start++;				   // 下标更新;
    		end--;					   // 下标更新;
    	}
    	
    	cout << "逆置后的数组为:" ;
    	for (int n = 0; n < 5; n++)
    	{
    		cout << arrx[n];
    	}
    	printf("\n");
    
        system("pause");
    	return 0;
    }
    
  • 冒泡排序

    1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个;
    2. 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值;
    3. 重复以上步骤,每次比较次数-1,直到不需要比较。
    image-20221014095059432
    • 排序总轮数 = 元素个数 - 1
    • 每轮对比次数 = 元素个数 - 排序轮数 - 1
    // 利用冒泡排序实现升序序列
    #include <iostream>
    
    // 冒泡排序函数 参数1 数组的首地址 参数2 数组长度
    void bubbleSort(int* arr, int len) {
        // 总共排序轮数为:数组长度-1
    	for (int i = 0; i < len - 1; i++) {
            // 每轮对比次数:数组长度-i-1
    		for (int j = 0; j < len - i - 1; j++) {
                // 前一个元素比后一个元素大则互换位置
    			if (arr[j] > arr[j + 1]) {
    				int temp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = temp;
    			}
    		}
    	}
    }
    
    // 打印数组
    void printArray(int* arr, int len) {
    	for (int i = 0; i < len; i++) {
    		std::cout << arr[i] << std::endl;
    	}
    }
    
    int main() {
    	// 1、创建数组
    	int arr[10] = { 4, 3, 6, 9, 1, 2, 10, 8, 7, 5 };
    	int len = sizeof(arr) / sizeof(arr[0]);
    
    	// 2、创建函数,实现冒泡排序
    	bubbleSort(arr, len);
    
    	// 3、打印排序后的数组
    	printArray(arr, len);
    
    	system("pause");
    	return 0;
    }
    

8.2 二维数组

  • 在一维数组的基础上多加一个维度

    int main(){
        // 1,数据类型 数组名[行数][列数];
        int arr[2][3];
    	arr[0][0] = 1;
    	arr[0][1] = 2;
    	arr[0][2] = 3;
    	arr[1][0] = 4;
    	arr[1][1] = 5;
    	arr[1][2] = 6;
        
        // 逐个输出
        cout << arr[0][0] << endl;
        cout << arr[0][1] << endl;
        ...
        cout << arr[1][2] << endl;
        // 利用嵌套循环逐个输出
        // 外层循环打印行数,内层循环打印列数
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 3; j++) {
                cout << arr[i][j] << endl;
            }
        }
    		
        // 2,数据类型 数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
        // 直观,代码可读性高,比较常用
        int arr2[2][3] = { 
    	    {1, 2, 3},
    	    {4, 5, 6}
    	};
        
        for (int i = 0; i < 2; i++) {
    		for (int j = 0; j < 3; j++) {
    			cout << arr2[i][j] << "  ";
    		}
    		cout << endl;
    	}
        
        // 3,数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};
        int arr3[2][3] = {11, 22, 33, 44, 55, 66};
        
    	for (int i = 0; i < 2; i++)
    	{
    		for (int j = 0; j < 3; j++)
    		{
    			cout << arr3[i][j] << "  ";
    		}
    		cout << endl;
    	}
    
        // 4,数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};
        int arr4[][3] = { 1, 2, 3, 4, 5, 6 };
        
    	for (int i = 0; i < 2; i++)	{
    		for (int j = 0; j < 3; j++)	{
    			cout << arr4[i][j] << "  ";
    		}
    		cout << endl;
    	}
        
        // 数组名称用途
        // 查看占用内存空间
        cout << "二维数组占用内存空间:" << sizeof(arr4) << endl;
        cout << "二维数组第一行占用内存空间:" << sizeof(arr4[0]) << endl; // 第0行
        cout << "二维数组第一个元素占用内存空间:" << sizeof(arr4[0][0]) << endl;
        
        cout << "二维数组的行数:" << sizeof(arr4) / sizeof(arr4[0]) << endl;
        cout << "二维数组的列数:" << sizeof(arr4[0]) / sizeof(arr4[0][0]) << endl;
        
        // 二维数组占用内存空间
    	cout << "二维数组的首地址:" << (int)arr4 << endl;
        cout << "二维数组第一行的首地址:" << (int)arr4[0] << endl;
        cout << "二维数组第二行的首地址:" << (int)arr4[1] << endl;
        
        cout << "二维数组第一个元素的首地址:" << (int)&arr4[0][0] << endl; // 访问具体元素的地址需要加地址符
        cout << "二维数组第二个元素的首地址:" << (int)&arr4[0][1] << endl;
    }
    

image-20240613115053265

  • 案例:考试成绩统计

    image-20240613115156319
    #include <iostream>
    #include <string>
    
    int main() {
        
    	int scores[3][3] = {
    		{100, 100, 100},
    		{90, 50, 100},
    		{60, 70, 80}
    	};
        
    	for (int i = 0; i < 3; i++)	{
    		for (int j = 0; j < 3; j++)	{
    			cout << scores[i][j] << "  ";
    		}
    		cout << endl;
    	}
    	
    	string names[3] = { "张三", "李四", "王五" }; //字符串;
    	// 每一行的三列进行相加得到总和
        for (int i = 0; i < 3; i++)	{
    		int sum = 0; // 统计分数总和变量
    		for (int j = 0; j < 3; j++)	{
    			sum += scores[i][j]; //sum = sum + scores[i][j]
    		}
    		cout << names[i] << "的总分是:" << sum << endl;
    	}
    
    	system("pause");
    	return 0;
    }
    

九、结构体

9.1 结构体定义与使用

  • 结构体属于用户自定义的数据类型,允许用户存储不同的数据

  • 语法:struct 结构体名{ 结构体成员列表 };

    • 定义的时候不可以省略掉struct,一般定义在main函数前或头文件里
  • 创建方式

    • 定义结构体时的关键字是struct,不可以省略
    • 创建结构体变量时,struct可以省略
    • 结构体变量利用操作符“.”访问成员
    #include <iostream>
    #include <string>
    using namespace std;
    
    // 1、创建学生数据类型:学生包括(姓名,年龄,分数)
    // 自定义数据类型,一些类型集合组成的一个类型
    struct Student{
        // 成员列表
        string name;	// 姓名
        int age;		// 年龄
        int score;		// 分数
    }s3;	// 2.3 定义结构体时顺便创建结构体变量s3(这种方式可读性差,不建议用)
    
    
    int main(){
        
        // 2、通过学生类型创建具体学生
        
    	// 2.1 struct Student s1
        struct Student s1;
        // 创建结构体变量的时候可以省略struct,但是定义的时候不可以省略
        Student s4;
        // 给s1属性赋值,通过“.”访问结构体变量中的属性
        s1.name = "张三";
        s1.age = 18;
        s1.score = 100;
        
        cout << "姓名:" << s1.name << "年龄:" << s1.age << "分数:" << s1.score << endl;
        
    	// 2.2 struct Student s2 = {...}
        // 创建结构体变量s2的时候赋初值
        struct Student s2 = {"李四", 19, 80};
        
        cout << "姓名:" << s2.name << "年龄:" << s2.age << "分数:" << s2.score << endl;
        
    	// 2.3 定义结构体时顺便创建结构体变量
        s3.name = "王五";
        s3.age = 18;
        s3.score = 80;
        
        cout << "姓名:" << s3.name << "年龄:" << s3.age << "分数:" << s3.score << endl;
        
    }
    

9.2 结构体数组

  • 作用:将自定义结构体放入到数组中方便维护

  • 语法:struct 结构体名 数组名[元素个数] = { {}, {}, ..., {} }

    #include <iostream>
    #include <string>
    using namespace std;
    
    // 1、结构体定义
    struct Student{
        string name;	// 姓名
        int age;		// 年龄
        int score;		// 分数
    };
    
    int main(){
        
        // 2、创建结构体数组
        // 可以初始化的时候赋初值,也可以创建后再赋值
        struct Student stuArray[3] = {
    		{"张三", 18, 120},
    		{"李四", 24, 80},
    		{"王五", 30, 95}
    	};
        
        // 3、给结构体数组中的元素赋值
        stuArray[2].name = "赵六";
        stuArray[2].age = 80;
        stuArray[2].score = 60;
        
        // 4、遍历结构体数组
        for (int i = 0; i < 3; i++) {
    		cout << "姓名:" << stuArray[i].name 
    			<< "年龄:" << stuarray[i].age 
    			<< "分数:" << stuarray[i].score << endl;
    	}
    }
    

9.3 结构体指针

  • 作用:通过指针访问结构体中的成员

  • 利用操作符->可以通过结构体指针访问结构体属性

    // 1、定义结构体变量
    struct Student{
        string name;	// 姓名
        int age;		// 年龄
        int score;		// 分数
    };
    
    int main(){
        // 2、创建结构体变量
        struct Student s = { "张三", 18, 100 };
        
        // 3、通过指针指向结构体变量
        struct Student * p = &s;
        
        // 4、通过指针访问结构体变量中的数据
        // 通过结构体指针访问结构体中的属性,需要利用操作符“->”进行访问
        cout << "姓名:" << p->name << "年龄:" << p->age << "分数:" << p->score << endl;
    }
    

9.4 结构体嵌套

  • 结构体中的成员可以是另外一个结构体

    #include <iostream>
    #include <string>
    using namesapce std;
    
    // 每个老师辅导一个学员,一个老师的结构体中记录一个学生结构体
    
    // 1、定义学生结构体(在老师结构体定义中要使用,因此要在定义老师结构体前先义学生结构体)
    struct Student{
        string name;	// 学生姓名
        int age;		// 学生年龄
        int score;		// 学生分数
    };
    
    // 2、定义老师结构体
    struct Teacher{
        int id;				// 教师编号
    	string name;		// 教师姓名
    	int age;			// 教师编号
    	struct Student stu;	 // 辅导的学生
    };
    
    int main(){
        
        Teacher t;			// 创建老师变量
        t.id = 10000;
        t.name = "老王";
    	t.age = 50;	
    	
    	t.stu.name = "小王";
    	t.stu.age = 20;
    	t.stu.score = 60;
        
        cout << "老师姓名:" << t.name << "老师编号:" << t.id << "老师年龄:" << t.age
             << "学生姓名:" << t.stu.name << "学生年龄:" << t.stu.age << "学生分数:" << t.stu.score 
             << endl;
    }
    

9.5 结构体做函数参数

  • 作用:将结构体作为参数向函数中传递

  • 传递方式:

    • 值传递(不会修改主函数中的数据)
    • 地址传递(可以修改主函数中的数据)
    #include <iostream>
    #include <string>
    using namesapce std;
    
    // 1、定义学生结构体
    struct Student{
        string name;	// 学生姓名
        int age;		// 学生年龄
        int score;		// 学生分数
    }
    
    // 打印学生信息函数
    // 1、值传递
    void printStudent1(struct Student stu){
        stu.age = 1000; // 主函数中的s.age不会被改变,只有形参发生变化
        cout << "子函数1中 姓名:" << stu.name << "年龄:" << stu.age << "分数:" << stu.score << endl;
    }
    
    // 2、地址传递
    // 形参用指针接受地址
    void printStudent2(struct Student * p){
        p->age = 1000; // 主函数中的s.age会被改变,形参和实参都发生变化
        cout << "子函数2中 姓名:" << p->name << "年龄:" << p->age << "分数:" << p->score << endl;
    }
    
    int main(){
        
        // 创建结构体变量
        struct Student s;
        s.name = "张三";
        s.age = 20;
        s.score = 85;
        
        cout << "main函数中打印 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;
        
        // 将结构体变量s传入到一个函数中,打印该结构体变量的信息
        // 值传递
        printStudent1(s);
        // 地址传递
        printStudent2(&s);
    }
    

9.6 结构体中const使用场景

  • 作用:防止误操作

    // const使用场景
    #include <iostream>
    #include <string>
    using namesapce std;
    
    // 定义结构体
    struct Student{
        string name;	// 学生姓名
        int age;		// 学生年龄
        int score;		// 学生分数
    }
    
    // 打印函数
    // 值传递会把结构体完全拷贝一份,如果这个结构体变量包含很大的数据量,那么值传递会将所有数据复制一份,导致占用大量内存
    void printStudent(struct Student stu){
        cout << "姓名:" << stu->name << "年龄:" << stu->age << "分数:" << stu->score << endl;
    }
    // 地址传递则只是传递地址,只占4个字节,从而减少内存空间,而且不会复制新的副本出来
    void printStudent(struct Student *stu){
        cout << "姓名:" << stu->name << "年龄:" << stu->age << "分数:" << stu->score << endl;
    }
    // 但是,地址传递存在一个隐患:在子函数中修改形参会直接修改主函数中的实参
    void printStudent(struct Student *stu){
        stu->age = 1000; // 实参也被修改!!!
        cout << "姓名:" << stu->name << "年龄:" << stu->age << "分数:" << stu->score << endl;
    }
    // 解决办法:加入const
    void printStudent(const struct Student *stu){
        // stu->age = 1000; // 修改操作会报错!!!防止误操作
        cout << "姓名:" << stu->name << "年龄:" << stu->age << "分数:" << stu->score << endl;
    }
    
    int main(){
        
        // 创建结构体变量
        struct Student s = {"张三", 15, 70};
        
        // 通过函数打印结构体变量信息
        printStudent(s);
    }
    

十、内存分区模型

  • C++程序在执行时,将内存大方向划分为4个区域

    • 代码区:存放函数体的二进制代码,由操作系统进行管理的
    • 全局区:存放全局变量和静态变量以及常量
    • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
    • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
  • 内存四区意义:

    • 不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程

10.1 程序运行前

  • 在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

    • 代码区
      • 存放 CPU 执行的机器指令
      • 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
      • 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
    • 全局区
      • 全局变量和静态变量存放在此
      • 全局区还包含了常量区, 字符串常量和其他常量也存放在此
      • 该区域的数据在程序结束后由操作系统释放
    //全局变量
    int g_a = 10;
    int g_b = 10;
    //全局常量
    const int c_g_a = 10;
    const int c_g_b = 10;
    int main() {
    	//局部变量
    	int a = 10;
    	int b = 10;
    	//打印地址
    	cout << "局部变量a地址为: " << (int)&a << endl;
    	cout << "局部变量b地址为: " << (int)&b << endl;
    
    	cout << "全局变量g_a地址为: " <<  (int)&g_a << endl;
    	cout << "全局变量g_b地址为: " <<  (int)&g_b << endl;
    
    	//静态变量
        // 可以使变量在程序执行期间只初始化一次,并在程序的整个执行期间都保持其值
        // 在函数内部声明的静态变量,它的生命周期是整个程序运行时间,但是它的可见范围仅限于声明它的函数
    	static int s_a = 10;
    	static int s_b = 10;
    
    	cout << "静态变量s_a地址为: " << (int)&s_a << endl;
    	cout << "静态变量s_b地址为: " << (int)&s_b << endl;
    
    	cout << "字符串常量地址为: " << (int)&"hello world" << endl;
    	cout << "字符串常量地址为: " << (int)&"hello world1" << endl;
    
    	cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
    	cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;
    
    	const int c_l_a = 10;
    	const int c_l_b = 10;
    	cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;
    	cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;
        
    	return 0;
    }
    
image-20221014115052733

10.2 程序运行后

  1. 栈区:

    • 由编译器自动分配释放,存放函数的参数值,局部变量等
    • 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
  2. 堆区:

    • 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
    • 在C++中主要利用new在堆区开辟内存
  3. new

    • 在堆区开辟数据

    • 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete

    • 语法:new 数据类型

      • 利用new创建的数据,会返回数据对应的类型的指针

        int* func() {
        	int* a = new int(10);
        	return a;
        }
        
        int main() {
        	int *p = func();
        
        	cout << *p << endl;
        	cout << *p << endl;
        
        	//利用delete释放堆区数据
        	delete p;
        
        	//cout << *p << endl; //报错,释放的空间不可访问
        
        	return 0;
        }
        
      • 开辟数组

        //堆区开辟数组
        int main() {
        	int* arr = new int[10];
        
        	for (int i = 0; i < 10; i++) {
        		arr[i] = i + 100;
        	}
        
        	for (int i = 0; i < 10; i++) {
        		cout << arr[i] << endl;
        	}
        	//释放数组 delete 后加 []
        	delete[] arr;
        
        	return 0;
        }
        

十一、通讯录管理系统

  • 主函数

    #include <iostream>
    #include "addressBooksSystem.h"
    
    using namespace std;
    
    int main() {
    
    	// 创建通讯录结构体变量
    	Addressbooks abs;
    	abs.m_Size = 0;
    
    	// 用户选择输入变量
    	int select = 0;
    
    	while (true) {
    		// 调用菜单
    		showMenu();
    
    		cin >> select;
    
    		switch (select) {
    		case 1:		// 1,添加联系人
    			addPerson(&abs);	// 利用地址传递可以修饰实参
    			break;
    		case 2:		// 2,显示联系人
    			showPerson(&abs);
    			break;
    		case 3:		// 3,删除联系人
    			deletePerson(&abs);
    			break;
    		case 4:		// 4,查找联系人
    			findPerson(&abs);
    			break;
    		case 5:		// 5,修改联系人
    			modifyPerson(&abs);
    			break;
    		case 6:		// 6,清空联系人
    			cleanPerson(&abs);
    			break;
    		case 0:
    			cout << "欢迎下次使用" << endl;
    			system("pause");
    			return 0;
    			break;
    		default:
    			break;
    		}
    	}
    
    	system("pause");
    	return 0;
    }
    
  • 头文件 addressBooksSystem.h

    #pragma once
    #include <iostream>
    #include <string>
    using namespace std;
    
    #define MAX 1000	// 宏常量,通讯录的最大容量,方便数据管理
    
    // 设计联系人结构体
    struct Person {
    
    	string m_Name;	// 姓名
    	int m_Sex;		// 性别,1-男,2-女
    	int m_Age;		// 年龄
    	string m_Phone;	// 电话
    	string m_Addr;	// 住址
    };
    
    // 设计通讯录结构体
    struct Addressbooks {
    
    	struct Person PersonArray[MAX]; // 联系人数组,定义一个宏常量MAX方便数据管理
    	int m_Size;						// 记录联系人个数
    };
    
    void showMenu();									// 菜单界面
    void addPerson(struct Addressbooks* abs);			// 添加联系人
    void showPerson(struct Addressbooks* abs);			// 显示所有联系人
    int isExist(struct Addressbooks* abs, string name);	// 检测联系人是否存在
    void deletePerson(struct Addressbooks* abs);		// 删除指定联系人
    void findPerson(struct Addressbooks* abs);			// 查找联系人
    void modifyPerson(struct Addressbooks* abs);		// 修改联系人信息
    void cleanPerson(struct Addressbooks* abs);			// 清空联系人
    
  • 源文件 addressBooksSystem.cpp

    #include "addressBooksSystem.h"
    
    // 菜单界面
    void showMenu() {
    
    	cout << "**************************" << endl;
    	cout << "*****  1,添加联系人  *****" << endl;
    	cout << "*****  2,显示联系人  *****" << endl;
    	cout << "*****  3,删除联系人  *****" << endl;
    	cout << "*****  4,查找联系人  *****" << endl;
    	cout << "*****  5,修改联系人  *****" << endl;
    	cout << "*****  6,清空联系人  *****" << endl;
    	cout << "*****  0,退出通讯录  *****" << endl;
    	cout << "**************************" << endl;
    }
    
    // 添加联系人
    void addPerson(struct Addressbooks* abs) {
    
    	// 判断通讯录是否已满,如果满了就不再添加
    	if (abs->m_Size == MAX) {
    		cout << "通讯录已满,无法添加!!!" << endl;
    		return;
    	}
    	else {
    		// 添加具体联系人
    		// 姓名
    		string name;
    		cout << "请输入姓名:" << endl;
    		cin >> name;
    		abs->PersonArray[abs->m_Size].m_Name = name;
    
    		// 性别
    		cout << "请输入性别:" << endl;
    		cout << "1 --- 男" << endl;
    		cout << "2 --- 女" << endl;
    
    		int sex = 0;
    
    		while (true) {
    			// 如果输入的是1或者2,可以退出循环,因为输入的是正确值
    			// 如果输入有误,则重新输入
    			cin >> sex;
    
    			if (sex == 1 || sex == 2) {
    				abs->PersonArray[abs->m_Size].m_Sex = sex;
    				break;
    			}
    
    			cout << "输入有误,请重新输入" << endl;
    		}
    
    		// 年龄
    		cout << "请输入年龄:" << endl;
    		int age = 0;
    		cin >> age;
    		abs->PersonArray[abs->m_Size].m_Age = age;
    
    		// 电话
    		cout << "请输入电话:" << endl;
    		string phone;
    		cin >> phone;
    		abs->PersonArray[abs->m_Size].m_Phone = phone;
    
    		// 住址
    		cout << "请输入家庭住址:" << endl;
    		string address;
    		cin >> address;
    		abs->PersonArray[abs->m_Size].m_Addr = address;
    
    		// 更新通讯录人数
    		abs->m_Size++;
    		cout << "添加成功" << endl;
    		// 请按任意键继续
    		system("pause");
    		// 清屏
    		system("cls");
    	}
    }
    
    // 显示所有联系人
    void showPerson(struct Addressbooks* abs) {
    
    	// 判断通讯录中人数是否为0,如果为0,提示记录为空
    	// 如果不为0,显示记录的联系人信息
    	if (abs->m_Size == 0) {
    		cout << "当前记录为空" << endl;
    	}
    	else {
    		for (int i = 0; i < abs->m_Size; i++) {
    			cout << "姓名:" << abs->PersonArray[i].m_Name << "\t";
    			cout << "性别:" << (abs->PersonArray[i].m_Sex == 1 ? "男" : "女") << "\t";
    			cout << "年龄:" << abs->PersonArray[i].m_Age << "\t";
    			cout << "电话:" << abs->PersonArray[i].m_Phone << "\t";
    			cout << "地址:" << abs->PersonArray[i].m_Addr << endl;
    		}
    	}
    
    	system("pause");
    	system("cls");
    }
    
    // 检测联系人是否存在,如果存在,返回联系人所在数组中的具体位置,不存在返回-1
    // 参数1 通讯录 参数2 对比姓名
    int isExist(struct Addressbooks* abs, string name) {
    
    	for (int i = 0; i < abs->m_Size; i++) {
    		// 找到用户输入的姓名了
    		if (abs->PersonArray[i].m_Name == name)
    			//返回这个人在数组中的下标编号
    			return i;
    	}
    	// 如果遍历所有人都没找到用户输入的姓名,则不存在,返回-1
    	return -1;
    }
    
    // 删除指定联系人
    void deletePerson(struct Addressbooks* abs) {
    
    	cout << "请输入您要删除的联系人" << endl;
    	string name;
    	cin >> name;
    
    	// ret == -1 未查到;ret != -1 查到了
    	int ret = isExist(abs, name);
    
    	if (ret != -1) {
    
    		// 查找到此人,要进行删除操作
    		for (int i = ret; i < abs->m_Size; i++) {
    			abs->PersonArray[i] = abs->PersonArray[i + 1]; //数据前移,执行删除
    		}
    		// 人数减一,更新通讯录人数
    		abs->m_Size--;
    		cout << "删除成功" << endl;
    	}
    	else {
    		cout << "查无此人" << endl;
    	}
    
    	system("pause");
    	system("cls");
    }
    
    // 查找联系人
    void findPerson(struct Addressbooks* abs)
    {
    	cout << "请输入您要查找的联系人姓名:" << endl;
    	string inputname;
    	cin >> inputname;
    
    	int ret = isExist(abs, inputname);
    
    	// 找到了联系人
    	if (ret != -1)
    	{
    		cout << "姓名:" << abs->PersonArray[ret].m_Name << "\t";
    		cout << "性别:" << (abs->PersonArray[ret].m_Sex == 1 ? "男" : "女") << "\t";
    		cout << "年龄:" << abs->PersonArray[ret].m_Age << "\t";
    		cout << "电话:" << abs->PersonArray[ret].m_Phone << "\t";
    		cout << "地址:" << abs->PersonArray[ret].m_Addr << endl;
    	}
    	// 未找到联系人
    	else {
    		cout << "查无此人" << endl;
    	}
    
    	system("pause");
    	system("cls");
    }
    
    // 修改联系人信息
    void modifyPerson(struct Addressbooks* abs) {
    
    	cout << "请输入您要修改的联系人姓名:" << endl;
    	string inputname;
    	cin >> inputname;
    
    	int ret = isExist(abs, inputname);
    
    	// 找到了
    	if (ret != -1) {
    		cout << "1:修改姓名" << endl;
    		cout << "2:修改性别" << endl;
    		cout << "3:修改年龄" << endl;
    		cout << "4:修改电话" << endl;
    		cout << "5:修改地址" << endl;
    		cout << "0:保存退出" << endl;
    
    		while (true) {
    
    			int modifyselect = -1;
    
    			cout << "请输入您的选择:" << endl;
    
    			cin >> modifyselect;
    
    			switch (modifyselect) {
    			case 1: {
    				string newName;
    				cout << "请输入修改后的姓名:" << endl;
    				cin >> newName;
    				abs->PersonArray[ret].m_Name = newName;
    			}
    				  break;
    
    			case 2: {
    				cout << "请输入修改后的性别:" << endl;
    				cout << "1 --- 男" << endl;
    				cout << "2 --- 女" << endl;
    				int newSex = 0;
    
    				while (true)
    				{
    					cin >> newSex;
    					if (newSex == 1 || newSex == 2)
    					{
    						abs->PersonArray[ret].m_Sex = newSex;
    						break;
    					}
    					else {
    						cin.clear();
    						cin.ignore(); //输入字母或中文里防止死循环
    						cout << "输入有误,请重新输入:" << endl;
    					}
    				}
    			}
    				  break;
    
    			case 3: {
    				cout << "请输入修改后的年龄:" << endl;
    				int newAge = 0;
    				while (true) //无限循环,直到输入正确再退出
    				{
    					cin >> newAge;
    					if (newAge > 0 && newAge < 120) {
    						abs->PersonArray[ret].m_Age = newAge;
    						break;
    					}
    					else {
    						cin.clear();
    						cin.ignore();
    						cout << "输入有误,请重新输入:" << endl;
    					}
    				}
    			}
    				  break;
    
    			case 4: {
    				cout << "请输入修改后的电话:" << endl;
    				string newPhone;
    				cin >> newPhone;
    				abs->PersonArray[ret].m_Phone = newPhone;
    			}
    				  break;
    
    			case 5: {
    				cout << "请输入修改后的地址:" << endl;
    				string newAddress;
    				cin >> newAddress;
    				abs->PersonArray[ret].m_Addr = newAddress;
    			}
    			break;
    
    			case 0:
    				cout << "修改成功" << endl;
    				system("pause");
    				system("cls");
    				return;
    				break;
    
    			default:
    				break;
    			}
    		}
    	}
    	else {
    		cout << "查无此人" << endl;
    	}
    
    	system("pause");
    	system("cls");
    }
    
    // 清空联系人
    void cleanPerson(struct Addressbooks* abs) {
    
    	// 将当前记录联系人数量置为0,做逻辑清空操作
    	abs->m_Size = 0;
    	cout << "通讯录已清空" << endl;
    
    	system("pause");
    	system("cls");
    }
    
image-20240615162556894

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/711706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Unity学习笔记】第十八 基于物理引擎的日月地系统简单实现

转载请注明出处: https://blog.csdn.net/weixin_44013533/article/details/139701843 作者&#xff1a;CSDN|Ringleader| 目录 目标数学理论资源准备数据准备代码实现Unity准备效果展示注意事项后记 目标 目标&#xff1a;利用Unity的物理引擎实现 “日地月三体系统” 。 效果…

【Android面试八股文】请描述new一个对象的流程

文章目录 请描述new一个对象的流程JVM创建对象的过程检查加载分配内存内存空间初始化设置对象初始化请描述new一个对象的流程 JVM创建对象的过程 当JVM遇到一条new指令时,它需要完成以下几个步骤: 类加载与检查内存分配 并发安全性内存空间初始化设置对象信息对象初始化下图…

upload-labs第十二关教程

upload-labs第十二关教程 一、源代码分析代码审计 二、绕过分析截断绕过magic_quotes_gpc函数介绍关闭magic_quotes_gpc上传eval.png文件使用burpsuite进行抓包修改放包&#xff0c;查看是否上传成功使用中国蚁剑进行连接 一、源代码分析 代码审计 $is_upload false; $msg …

网络协议,OSI,简单通信,IP和mac地址

认识协议 1.讲故事 2004年&#xff0c;小明因为给他爹打电话&#xff08;座机&#xff09;费用太贵&#xff0c;所以约定一种信号&#xff0c;响一次是报平安&#xff0c;响两次是要钱&#xff0c;响三次才需要接通。 2.概念 协议&#xff1a;是一种约定&#xff0c;这种约…

基于CentOS Stream 9平台安装MySQL8.4.0 LTS

1. 安装之前 1.1 查看系统版本 [rootcoisini /]# cat /etc/redhat-release CentOS Stream release 9 1.2 查看cpu架构 [rootcoisini /]# lscpu 架构&#xff1a; x86_64 CPU 运行模式&#xff1a; 32-bit, 64-bit 2. MySQL官方下载https://dev.mysql.com/downloads/mysql/ 或…

Web期末复习指南(2w字总结)

前言&#xff1a;本文旨在梳理Web技术常包含的内容&#xff0c;阅读完整篇文章后会对整体有个系统的认知&#xff0c;从前端的HTML到后端的MySql&#xff0c;对于大概试题中可能会涉及的地方都有所分析&#xff0c;通篇提供了许多代码案例&#xff0c;供读者更好的理解。对于一…

MongoDB~事务了解;可调一致性模型功能与因果一致性模型功能分析

背景 MongoDB 从 3.0版本引入 WiredTiger 存储引擎之后开始支持事务&#xff0c;MongoDB 3.6之前的版本只能支持单文档的事务&#xff0c;从 MongoDB 4.0版本开始支持复制集部署模式下的事务&#xff0c;从 MongoDB 4.2版本开始支持分片集群中的事务。 根据官方文档介绍&…

代码随想录算法训练营第二十四天| (回溯) 77. 组合、 216.组合总和III、17.电话号码的字母组合

77. 组合 题目链接&#xff1a;77. 组合 文档讲解&#xff1a;代码随想录 状态&#xff1a;很多细节忘了 思路&#xff1a;先画图&#xff0c;然后可以发现&#xff0c;从1到n中选择k个数&#xff0c;可以看成是一个递归过程&#xff0c;这个递归的深度就是k。然后遍历当前这层…

企业化运维(2)_nginx

###1.nginx源码安装部署### ###2.平滑升级### &#xff08;1&#xff09;版本升级 当服务器在运行时&#xff0c;需要升级的情况下&#xff0c;平滑升级即就是不断开服务器就可以进行升级&#xff0c;最大限度保证数据的完整性。 下载nginx新版本软件&#xff0c;正常执行./c…

简易五子棋

简介 使用Java实现简易五子棋 规则介绍 游戏使用一个标准的1515方格的棋盘&#xff0c;双方分别用黑白两种颜色的棋子进行对战。黑子先行&#xff0c;双方轮流在空棋盘的交叉点上落子&#xff0c;每人一次只能落一子。游戏的目标是让自己的五颗棋子连成一线&#xff0c;这条…

JavaScript-函数

学习目标&#xff1a; 掌握函数 学习内容&#xff1a; 为什么需要函数函数使用函数传参函数返回值函数细节补充函数作用域匿名函数案例 为什么需要函数&#xff1a; 函数&#xff1a;function 是被设计为执行特定任务的代码块。说明&#xff1a;函数可以把具有相同或相似逻辑…

探索交互的本质:从指令到界面的演进与Linux基础指令的深入剖析

目录 1.指令 vs 界面//选读 1.1交互的需求 满足需求的第一阶段-指令 满足需求的第二阶段-界面 1.2 指令 和 界面交互 区别 2.操作系统介绍 2.1 举例说明 驱动软件层 2.2 为什么要有操作系统&#xff1f; 0x03 为什么要进行指令操作&#xff1f; 3.Linux基本指令 l…

Linux基础命令[29]-chown

文章目录 1. chown 命令说明2. chown 命令语法3. chown 命令示例3.1 修改属主3.2 修改属组3.3 修改属主和属组3.4 修改文件夹所属 4. 总结 1. chown 命令说明 chown&#xff1a;更改文件的用户或用户组&#xff0c;需要 root 用户或 sudo 权限的用户执行该命令。基本信息如下&…

深度学习(PyTorch)批注理解,建议边学可以边看这个笔记

前言 动手学习深度学习&#xff0c;内容丰富&#xff0c;但是对于初学者有很多晦涩难懂的地方&#xff0c;我将日常更新这篇文章以截图的形式&#xff0c;每天高强度学习四五个小时&#xff0c;精力缺乏&#xff0c;我认为&#xff0c;如果想学习这个深度学习&#xff0c;你需…

微信公众号打通与登录的实现

今天实现一下与微信公众号进行对接&#xff0c;通过扫描二维码的方式来进行注册与登录&#xff0c;获取用户的微信唯一标识作为用户的username&#xff0c;下面我们开始编写。 骨架建立&#xff1a; 建包&#xff1a; 第一步还是先将骨架建好&#xff0c;与网关骨架差不多&a…

堆栈溢出的攻击 -fno-stack-protector stack smash 检测

在程序返回的一条语句堆栈项目处&#xff0c;用新函数的起始地址覆盖&#xff0c;将会跳转到执行新函数。 现在系统对这个行为做了判断&#xff0c;已经无法实施这类攻击或技巧。 1&#xff0c;测试代码 #include <stdio.h> void cc() {printf("I am cc( )\n"…

Boom 3D软件下载及安装教程

简介&#xff1a; Boom 3D是适用于Mac和Windows系统的专业音效增强软件&#xff0c;旨在通过播放器&#xff0c;媒体或流媒体服务等介质&#xff0c;在不同类型的耳机上以3D环绕效果播放媒体内容。您无需使用昂贵的耳机或其他附加环绕音效增强器即可感受3D环绕音乐。 安 装 包…

【Python推导式秘籍】:一行代码的艺术,高效数据处理之道

文章目录 &#x1f68b;Python推导式&#x1f680;一、列表推导式&#x1f308;1. 了解推导式❤️2. 实践&#x1f4a5;3. 总结 &#x1f680;二、字典推导式&#x1f308;1. 了解字典推导式❤️2. 实践&#x1f4a5;3. 总结 &#x1f680;三、集合推导式&#x1f308;1. 了解集…

liunx常见指令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 二、安装环境 1.租借服务器 2.下载安装 XShell 3.使用xshll登录服务器 三、Linux基础命令 一、文件和命令 ​编辑1、cd 命令 2、pwd 命令 3、ls 命令 4、cp 命令 …

人工智能GPU互联技术分析,芯片巨头UALink向英伟达NVLink开战

芯片巨头组团&#xff0c;向英伟达NVLink开战 八大科技巨头——AMD、博通、思科、Google、惠普企业、英特尔、Meta及微软——联合推出UALink&#xff08;Ultra Accelerator Link&#xff09;技术&#xff0c;为人工智能数据中心网络设定全新互联标准。此举旨在打破Nvidia的市场…