C++零基础入门笔记(简洁版)

一、c++程序基本框架

#include <iostream>
using namespace std;
int main()
{
	
} 

代码解读

  1. 第一行 #include <iostream>:这是头文件,我们需要iostream这个库,它包含输入输出信息流。
  2. using namespace std;:这是命名空间,要去哪里执行任务的意思。
  3. int main(){ }:这是主函数,花括号包围的部分就是函数体,里面是我们编写的指令。
  • 【注意】:程序的执行始于对 main 函数的调用。当你运行一个C++程序时,操作系统会加载程序到内存中,然后跳转到 main 函数的开始处,并从此处开始执行程序。这个过程是由操作系统和运行时环境来控制的,而不是由C++语言本身自动完成的。

二、输入输出

  • cout:输出
#include <iostream>
using namespace std;
int main()
{
	cout<<"Hello World"<<endl; //endl:表示换行
	cout<<"Hello C++"<<endl;
} 

结果:
在这里插入图片描述

  • cin:输入
#include <iostream>
using namespace std;
int main()
{
	int a; //声明一个变量a 
	cin>>a; //把输入的值给a变量
	cout<<"a="<<a;
} 

假如键盘输入6,结果:
在这里插入图片描述

  • 连续输入输出
#include <iostream>
using namespace std;
int main()
{
	int a; //声明变量a 
	int b; //声明变量b
	cin>>a>>b; //输入a和b
	cout<<a<<" "<<b; //输出a和b 
} 

假如键盘输入2,回车再输入8,结果:
在这里插入图片描述

三、变量的声明和赋值

0.变量名的规则

  • 【规则1】:变量名包含字母、数字、下划线,但数字不能在变量名首位,例如以下变量名:

a(合法)、a123(合法)、_xyz(合法)、2b(不合法,不能以数字开头)

  • 【规则2】:具有特殊含义的单词不能作为变量名,否则会报错,因为它们在C++中已经代表了特定的意思,例如以下单词:

while, int, if, char, long, bool 等等。

1.变量的声明

  • 想要使用变量,必须先做“声明”,也就是告诉计算机要用到的数据叫什么名字。变量声明的标准语法可以写成:数据类型 变量名;
#include <iostream>
using namespace std;
int main()
{
	int a; //声明一个变量a 
	cin>>a; //把输入的值给a变量
	cout<<"a="<<a;
} 

结果:
在这里插入图片描述

2.变量的声明且赋值

#include <iostream>
using namespace std;
int main()
{
	int a = 1; //声明变量a,并赋值为1 
	cout<<a; //结果:输出1
}

3.全局变量和局部变量

#include <iostream>
using namespace std;

// 全局变量
int number = 100;

int main()
{
	// 局部变量
	int number = 1;

	// 访问局部变量
	cout << "number = " << number << endl;
	// 访问全局变量,number前面要加上双冒号:: 
	cout << "number = " << ::number << endl; 

}
 

在这里插入图片描述

4.常量

常量:一旦创建后,则不能修改它的值,否则会报错。语法:const 数据类型 变量名 = xxx

#include <iostream>
using namespace std;
int main()
{
	// 定义常量,不能修改常量值 
	const int Zero = 0;
	cout<<Zero;
}

5.交换两个变量的值

#include <iostream>
using namespace std;
int main()
{
	int a=10;
	int b=20;
	
	int t;//创建一个临时变量t 
	t=a;//把a盒子中的数字拷贝到t盒子
	a=b;//把b盒子中的数字拷贝到a盒子
	b=t;//把t盒子中的数字拷贝到b盒子
	cout<<"a="<<a<<endl;
	cout<<"b="<<b;	
}

结果:
在这里插入图片描述

四、基本数据类型

  • C++支持丰富的数据类型,它内置了一套基本数据类型,主要包括算术类型和空类型(void)。算术类型又包含了整型和浮点型;而空类型不对应具体的值,只用在一些特定的场合,比如一个函数如果不返回任何值,我们可以让void作为它的返回类型。

C++定义的整型包括以下几种:
在这里插入图片描述

#include <iostream>
using namespace std;
int main()
{
	// 常用整型 
	int a = 1;
	cout << "a = " << a << endl;
	
	// 浮点类型 
	float f = 3.14;
	cout << "f = " << f << endl;
	
	// 布尔类型:只有两个取值,true和false
	bool bl = true; 
	cout << "bl = " << bl << endl;	
}
 

结果:
在这里插入图片描述

  • 数据类型转换
#include <iostream>
using namespace std;
int main() {
	// 1. 整数值赋给bool整型
	bool b = 25; 
	cout<<"b="<<b<<endl;

	// 2. bool类型赋值给整型
	int s = false; 
	cout<<"s="<<s<<endl;
	
	// 3. 浮点数赋给整数类型
	int i = 3.14; 
	cout<<"i="<<i<<endl;
	
	// 4. 整数值赋给浮点类型
	float f = 10; 
	cout<<"f="<<f<<endl;
}

结果:
在这里插入图片描述

  • 数据类型转换规则总结如下:
  • 非布尔类型的算术值赋给布尔类型,初始值为0则结果为 false , 否则结果为true ;
  • 布尔值赋给非布尔类型,初始值为 false 则结果为0,初始值为 true 则结果为1;
  • 浮点数赋给整数类型,只保留浮点数中的整数部分,会带来精度丢失;
  • 整数值赋给浮点类型,小数部分记为0。如果保存整数需要的空间超过了浮点类型的容量,可能会有精度丢失。

五、运算符

1.算术运算符

  • C++中有5种算术运算符:
    在这里插入图片描述
#include <iostream>
using namespace std;
int main()
{
	// 加法
	int a = 20, b = 6;
	cout << " a + b = " << a + b << endl;
	
	// 减法
	cout << " a - b = " << a - b << endl;
	
	// 乘法
	cout << " a * b = " << a * b << endl;
	
   	// 除法
	cout << " a / b = " << a / b << endl;

	// 取模:两个数必须是整数类型
	cout << " a % b = " << a % b << endl;
} 

结果:
在这里插入图片描述

  • 以上5种算术运算符优先级顺序如下:
运算符优先级
* / %三者并列,高于加减
+ -两者并列
#include <iostream>
using namespace std;
int main() {
	//优先级比较
	int a = 20, b = 6,c = 10;
	cout << " a + c * b = " << a + c * b << endl;
	cout << " a % b * c = " << a % b * c << endl;
	cout << " a / b % c = " << a / b % c << endl;
	cout << " a * b / c = " << a * b / c << endl;
}

结果:
在这里插入图片描述

  • 算术表达式:由算术运算符和数字组成的式子,例如a + 1,a + b * c等等。

3.赋值运算符

  • 在C++中,用等号“=”表示一个赋值操作,这里的“=”就是赋值运算符。
#include <iostream>
using namespace std;
int main() {
	int a = 20;//把20赋值给a变量 
	cout << " a = " << a << endl;
	int c = a = 100;    // 连续赋值:把100重新赋值给a变量,再把a变量的值赋值给c变量 
	cout << " c = " << c << endl;
}

结果:
在这里插入图片描述

4.复合赋值运算符

在这里插入图片描述

#include <iostream>
using namespace std;
int main() {
	int x = 6,y = 2;
	y+=x; //等价于 y = y+x; 即先计算y+x的结果,再把结果赋值给y,以下同理 
	cout<<"y="<<y<<endl;
	x-=5;
	cout<<"x="<<x<<endl;
	x/=y;
	cout<<"x="<<x<<endl;
}

结果:
在这里插入图片描述

5.自增自减运算符

  • “自增”用两个加号“++”表示,表示“对象值加一,再赋值给原对象”;“自减”则用两个减号“–”表示。
++a;    // a自增,相当于 a += 1;
--b;    // b自减,相当于 b -= 1;
  • 自增自减运算符各自有两种形式:“前置”和“后置”,也就是说写成“++a”和“a++”都是可以的。它们都表示“a = a + 1”,区别在于表达式返回的结果不同:
  • 前置时,对象先加1,再将更新之后的对象值作为结果返回;
  • 后置时,对象先将原始值作为结果返回,再加1;
#include <iostream>
using namespace std;
int main() {
	int x = 3;
	int y = ++x;//先让x自增1,再将x更新后的结果赋值给y
	cout<<"x="<<x<<endl;
	cout<<"y="<<y<<endl;

	int a = 3;
	int b = a++;//先将a赋值给b,再让a自增1
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;
}

结果:
在这里插入图片描述

6.关系运算符

  • 关系运算符:两个表达式可以使用关系运算符进行比较,运算结果是布尔类型值:true或false。
    在这里插入图片描述
1 < 2;    // true
3 >= 5;    // false
10 == 4 + 6;    // true
(10 != 4) + 6;    // 7 原因:关系运算符的返回值为布尔类型,如果参与算术计算,true的值为1,false的值为0;

注意:在C++语法中一个等号“=”表示的是赋值,两个等号“==”才是真正的“等于”。

7.逻辑运算符

  • 逻辑运算符:一个关系运算符的结果是一个布尔类型(ture或者false),就可以表示一个条件的判断;如果需要多个条件的叠加,就可以用逻辑“与或非”将这些布尔类型组合起来。这样的运算符叫做“逻辑运算符”。
  • 逻辑非(!):将运算对象的值取反后返回;
  • 逻辑与(&&):两个运算对象都为true时结果为true,否则结果为false;
  • 逻辑或(||):两个运算对象只要有一个为true结果就为true,都为false则结果为false;
1 < 2 && 3 >= 5;    // false
1 < 2 || 3 >= 5;    // true
!(1 < 2 || 3 >= 5);    // false
  • 逻辑与和逻辑或有两个运算对象,在计算时都是先求左侧对象的值,再求右侧对象的值;如果左侧对象的值已经能决定最终结果,那么右侧就不会执行计算:这种策略叫做“短路求值”;看下面例子:
#include <iostream>
using namespace std;
int main() {
	int i = -1;
	1 < 2 && ++i; // false
	cout << " i = " << i << endl; // i = 0
	1 < 2 || ++i; // true
	cout << " i = " << i << endl; // i = 0
}

8.条件运算符

  • “条件运算符”:由“?”和“:”两个符号组成,需要三个运算表达式,形式如下:
条件判断表达式 ? 表达式1 : 表达式2

条件运算符的含义是:计算条件判断表达式的值,如果为true就执行表达式1,返回求值结果;如果为false则跳过表达式1,执行表达式2,返回求值结果。

#include <iostream>
using namespace std;
int main() {
	int i = 0;
	cout << ((1 < 2 && ++i) ? "true" : "false") << endl; //结果:true 
}

注意:条件运算符的优先级比较低,所以输出的时候需要加上括号。事实上,条件运算符等同于流程控制中的分支语句if…else…,只用一条语句就可以实现按条件分支处理,这就让代码更加简洁。


9.总结:运算符的优先顺序

运算符说明C++运算符优先级顺序
小括号( )高——>低
逻辑非运算符、自增自减运算符!++ - -
算术运算符* / %
算术运算符+、-
关系运算符>、>=、<、<=
关系运算符==、!=
逻辑与运算符&&
逻辑或运算符||
赋值运算符= 、+=、 -= 、*= 、/=、 %=

六、if条件判断

  • if 语句主要就是判断一个条件是否为真(true),如果为真就执行下面的语句,如果为假则跳过。具体形式可以分为两种:一种是单独一个if,一般称为“单分支”;另一种是if
    … else …,称为“双分支”。

1.单分支

#include<iostream>
using namespace std;
int main() {
	cout << "请输入您的年龄:" << endl;
	int age;
	cin >> age;
	if ( age >= 18 ) {
		cout << "欢迎您,成年人!" << endl;
	}
}

结果:
在这里插入图片描述

2.双分支

  • 双分支就是在if分支的基础上,加了else分支;条件为真就执行if后面的语句,条件为假就执行else后面的语句。
#include<iostream>
using namespace std;
int main() {
	cout << "请输入您的年龄:" << endl;
	int age;
	cin >> age;
	if ( age >= 18 ) {
		cout << "欢迎您,成年人!" << endl;
	} else {
		cout << "本程序不欢迎未成年人!" << endl;
	}
}

结果:
在这里插入图片描述

3.多层嵌套分支if … else …

#include<iostream>
using namespace std;
int main() {
	cout << "请输入您的年龄:" << endl;
	int age;
	cin >> age;
	if ( age >= 18 ) {
		cout << "欢迎您,成年人!" << endl;
		if (age < 35) {
			cout << "加油,年轻人!" << endl;
		}
	} else {
		cout << "本程序不欢迎未成年人!" << endl;
		if (age >= 12) {
			cout << "少年,好好学习!" << endl;
		} else {
			cout << "小朋友,别玩电脑!" << endl;
		}
	}
}

结果:
在这里插入图片描述

4.多层嵌套分支if … else if…

嵌套分支如果比较多,代码的可读性会大幅降低。所以还有一种更加简单的嵌套分支写法,那就是if … else if …,具体形式如下:

#include<iostream>
using namespace std;
int main() {
	cout << "请输入您的年龄:" << endl;
	int age;
	cin >> age;
	if (age < 12) {
		cout << "小朋友,别玩电脑!" << endl;
	} else if (age < 18) {
		cout << "少年,好好学习!" << endl;
	} else if (age < 35) {
		cout << "加油,年轻人!" << endl;
	} else if (age < 60) {
		cout << "加油,中年人!" << endl;
	} else {
		cout << "好好休息,老年人!" << endl;
	}
}

结果:
在这里插入图片描述
练习:输入3个数,输出三个数中的最大值

七、循环

  • C++中的循环语句,有while、do while和for三种。

1.while循环

  • while只需要给定一个判断条件,只要条件为真,就重复地执行语句。语法如下:
while (条件){
	语句
}

需求1:输出1~3之间所有的整数,并用空格隔开。

#include<iostream>
using namespace std;
int main() {
	int i = 1;
	while (i <= 3) {
		cout<<i<<" "; 
		i++;
	}
}

结果
在这里插入图片描述
分析代码:拆解以上while循环的执行过程

第一次循环:
	i = 1; //i的初始值为1
	i <= 3 //条件成立,执行循环体中的语句
		cout << i << " "; //此时输出1
		i++; //i的值变为2
第二次循环:
	i = 2; //因为第一次循环结束后,i的值变为2
	i <= 3 //条件成立,执行循环体中的语句
		cout << i << " "; //此时输出2
		i++; //i的值变为3
第三次循环:
	i = 3; //因为第二次循环结束后,i的值变为3
	i <= 3 //条件成立,执行循环体中的语句
		cout << i << " "; //此时输出3
		i++; //i的值变为4
第四次循环:
	i = 4; //因为第三次循环结束后,i的值变为4
	i <= 3 //条件不成立,本次循环体并不会执行,也就是循环到此终止了

需求2:计算出1~10之间所有整数的和。

#include<iostream>
using namespace std;
int main() {
	int sum = 0;
	int i = 1;
	while (i <= 10) {
		sum += i;
		i++;
	}
	cout<<"sum="<<sum;
}

结果:
在这里插入图片描述

2.do while循环

do while和while非常类似,区别在于do while是先执行循环体中的语句,然后再检查条件是否满足。所以do while至少会执行一次循环体。do while语法如下:

do {
	语句
} while (条件);

需求1:输出1~10之间所有的整数,并用空格隔开。

#include<iostream>
using namespace std;
int main() {
	int i = 1;
	do {
		cout<<i<<" ";
		i++;
	} while(i<11);
}

结果:
在这里插入图片描述

3.for循环

  • for循环的经典语法形式是:
for (初始化语句; 条件; 表达式){
	语句
}
  • for循环的执行顺序:

for语句头中有三部分,用分号分隔,主要作用是:

  • 初始化语句负责初始化一个变量,这个变量值会随着循环迭代而改变,一般就是“循环变量”。
  • 中间的条件是控制循环执行的关键,为真则执行下面的循环体语句,为假则退出。
  • 最后的表达式会在本次循环完成之后再执行,一般会对循环变量进行更改;

这三个部分并不是必要的,根据需要都可以进行省略。如果省略某个部分,需要保留分号表示这是一个空语句。

需求1:输出1~3之间所有的整数,并用空格隔开。

#include<iostream>
using namespace std;
int main() {
	for (int i = 1; i <= 3; i++)
	{	
		cout << i << " ";
	}
}

结果:

在这里插入图片描述
分析代码:拆解以上for循环的执行过程

执行顺序示意图:

在这里插入图片描述

第一次循环:
	i = 1; //i的初始值为1
	i <= 3 //条件成立,执行循环体中的语句
		cout << i << " "; //此时输出1
	i++; //i的值变为2
第二次循环:
	i = 2; //因为第一次循环结束后,i的值变为2
	i <= 3 //条件成立,执行循环体中的语句
		cout << i << " "; //此时输出2
	i++; //i的值变为3
第三次循环:
	i = 3; //因为第二次循环结束后,i的值变为3
	i <= 3 //条件成立,执行循环体中的语句
		cout << i << " "; //此时输出3
	i++; //i的值变为4
第四次循环:
	i = 4; //因为第三次循环结束后,i的值变为4
	i <= 3 //条件不成立,本次循环体并不会执行,也就是循环到此终止了

需求2:计算出1~10之间所有整数的和。

#include<iostream>
using namespace std;
int main() {
	int sum = 0;
	for (int i = 1; i <= 10; i++)
	{	
		sum += i;
	}
	cout<<"sum="<<sum;
}

结果:
在这里插入图片描述

4.循环嵌套

  • 循环语句和分支语句一样,也是可以进行嵌套的。具体可以while循环中嵌套while,可以for循环中嵌套for,也可以while、do while和for混合嵌套。因为for的循环变量定义更明确,所以一般用双for循环嵌套会多一些。
#include<iostream>
using namespace std;
int main() {
	for (int i = 0; i < 2; i++) { //外层循环 
		for (int j = 0; j < 3; j++) { //内层循环
			cout << "Hello World!" << endl;
		}
	}
}

结果:
在这里插入图片描述
分析代码:拆解以上循环嵌套的执行过程

第一次外层循环:
	i = 0; //i的初始值为0
	i < 2  //条件成立,进入内层循环
		第一次进入内层循环:
			【内层循环第一次:】
			j = 0; //j的初始值为0
			j < 3  //条件成立
				cout << Hello World! << endl; //此时输出一次 Hello World!
			j++; //j的值变为1
			【内层循环第二次:】
			j = 1; 
			j < 3  //条件成立
				cout << Hello World! << endl; //此时输出一次 Hello World!
			j++; //j的值变为2
			【内层循环第三次:】
			j = 2; 
			j < 3  //条件成立
				cout << Hello World! << endl; //此时输出一次 Hello World!
			j++; //j的值变为3
			【内层循环第四次:】
			j = 3; 
			j < 3  //条件不成立,内层循环结束
	i++; //i的值变为1
第二次外层循环:
	i = 1; 
	i < 2  //条件成立,进入内层循环
		第二次进入内层循环:
			【内层循环第一次:】
			j = 0; //j的初始值为0
			j < 3  //条件成立
				cout << Hello World! << endl; //此时输出一次 Hello World!
			j++; //j的值变为1
			【内层循环第二次:】
			j = 1; 
			j < 3  //条件成立
				cout << Hello World! << endl; //此时输出一次 Hello World!
			j++; //j的值变为2
			【内层循环第三次:】
			j = 2; 
			j < 3  //条件成立
				cout << Hello World! << endl; //此时输出一次 Hello World!
			j++; //j的值变为3
			【内层循环第四次:】
			j = 3; 
			j < 3  //条件不成立,内层循环结束
	i++; //i的值变为2
第三次外层循环:
	i = 2; 
	i < 2  //条件不成立,外层循环结束,整个循环都终止了

练习:九九乘法表

八、语句跳转

  • 在流程控制语句中还有一类“跳转语句”,主要用来中断当前的执行过程。C++中有四种跳转语句:break,continue,goto以及return。这里主要讲 break 和 continue。

1.break

  • 当代码中遇到break时,会直接中断距离最近的循环,跳转到外部继续执行。
#include <iostream>
using namespace std;
int main() {
	int i = 1;
	while (true) {
		cout << " 这是第" << i << "次输出" << endl;
		i++;
		if (i > 5) {
			break;
		}	
	}
}

结果:
在这里插入图片描述

2.continue

  • continue语句表示“继续”执行循环,也就是中断循环中的本次迭代,并开始执行下一次迭代。
//需求:输出1到10之间所有的整数,5不输出

#include <iostream>
using namespace std;
int main() {
	for(int i = 1; i<=10; i++) {
		if (i == 5) {
			continue;
		} else {
			cout << "这是第" << i << "次输出" << endl;
		}
	}
}

结果:
在这里插入图片描述

九、枚举算法

  • 枚举的定义:根据所需解决问题的条件,把该问题所有可能的解,一一列举出来,并逐个检验出问题真正解的方法。枚举法也称穷举法。

(1)判断水仙花数

水仙花数:指一个 n 位数(n≥3),它的每个位上的数字的 n 次幂之和等于它本身。例如,153是一个水仙花数,因为1^3 + 5^3 + 3^3 = 153。

题目:找出100~999整数中的所有水仙花数.

  • 方法一:使用while循环
#提示:“/”表示除法,“%”表示取余
#include <iostream>
#include <cmath>
using namespace std;
int main() {
	int num = 100;
	while(num<1000) {
		int a = num/100;
		int b = num%100/10;
		int c = num%10;
		if(pow(a,3) + pow(b,3) + pow(c,3) == num) {
			cout<<num<<"是一个水仙花数"<<endl;
		}	
		num+=1;
	}
}

在这里插入图片描述

(2)鸡兔同笼

有一个笼子,里面有鸡和兔子。我们知道总共有7个头和18只脚,我们要找出有多少只鸡和多少只兔子。

假设笼子里有 x 只鸡和 y 只兔子。根据题目,我们可以建立以下方程:

  1. 头的总数是 x + y = 7(鸡和兔子的头数加起来)。
  2. 脚的总数是 2x + 4y = 18(鸡有2只脚,兔子有4只脚,总脚数就是2倍的鸡脚数加上4倍的兔脚数)。

现在我们要来解这个方程组,找出 x 和 y 的值。计算结果为: {x: 5, y: 2}。所以,笼子里有 5 只鸡和 2 只兔子。

以上我们用的是数学中列举方程的形式求解,我们也可以利用枚举法,通过代码帮我们计算最终的结果。

  • 枚举的思路如图所示:一一列举,最终得到总的脚数量为18的组合,答案即为5 只鸡和 2 只兔子。
在这里插入图片描述
//使用while循环求解
#include <iostream>
using namespace std;
int main() {
	int checkin = 0;
	int rabbit = 0;
	while(true) {
		if(2*checkin+4*rabbit == 18) {
			break;
		}
		checkin += 1;
		rabbit = 7-checkin;
	}
	cout<<"鸡的数量:"<<checkin<<"  "<<"兔的数量:"<<rabbit<<endl;
}

结果:
在这里插入图片描述

(3)因式分解

题目:有两个两位数,他们的乘积等于1691,求这两个数分别是多少?

#include <iostream>
using namespace std;
int main() {
	for(int i = 10;i < 100; i++) {
		for(int j = 10;j < 100; j++) {
			if(i*j == 1691) {
				cout<<i<<" "<<j<<endl;
				break;
			}
		}
	}
}

在这里插入图片描述

思考:以上结果为何会输出两遍?代码能否进行优化呢?

代码优化:

#include <iostream>
using namespace std;
int main() {
	for(int i = 10;i < 100; i++) {
		for(int j = i;j < 100; j++) {
			if(i*j == 1691) {
				cout<<i<<" "<<j<<endl;
				break;
			}
		}
	}
}

在这里插入图片描述

十、复合类型数据

1.数组

数组:在程序中为了处理方便,常常需要把具有相同类型的数据对象按有序的形式排列起来,形成“一组”数据,这就是“数组”(array)。
在这里插入图片描述
数组中的数据,在内存中是连续存放的,每个元素占据相同大小的空间,就像排好队一样。

1.数组的定义

数组的定义形式如下:

数据类型 数组名[元素个数];
int a1[10];  // 定义一个数组a1,元素类型为int,个数为10

数组定义的规则:

  • 1.首先需要声明类型,数组中所有元素必须具有相同的数据类型;
  • 2.数组名是一个标识符;后面跟着中括号,里面定义了数组中元素的个数,也就是数组的“长度”;
  • 3.元素个数也是类型的一部分,所以必须是确定的;
2.数组的初始化
int a3[4] = {1,2,3,4};
float a4[] = {2.5, 3.8, 10.1}; // 正确,初始值说明了元素个数是3

重点:

  • 1.对数组做初始化,要使用花括号{}括起来的数值序列;
  • 2.如果做了初始化,数组定义时的元素个数可以省略,编译器可以根据初始化列表自动推断出来;
  • 3.初始值的个数,不能超过指定的元素个数;
  • 4.初始值的个数,如果小于元素个数,那么会用列表中的值初始化靠前的元素;剩余元素用默认值填充,整型的默认值就是0;
  • 5.如果没有做初始化,数组中元素的值都是未定义的;这一点和普通的局部变量一致;

以上重点中的第四点,举例如下:

#include<iostream>
using namespace std;
int main() {
	int a[6] = {1,2,3,4};
	int len = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < len; i++) {
		cout << a[i] << " ";
	}
}

结果:
在这里插入图片描述

3.数组的访问
(1)访问数组元素

数组元素在内存中是连续存放的,它们排好了队之后就会有一个队伍中的编号,称为“索引”,也叫“下标”;通过下标就可以快速访问每个元素了,具体形式为:

数组名[元素下标]

例如下面这a1个数组:
在这里插入图片描述

#include<iostream>
using namespace std;
int main() {
	int a[4] = {5,2,6,4};
	cout << a[2] << endl; //6
	a[2] = 36; //对a[2]重新赋值
	cout << a[2]; //36
}

注意:

  • 1.数组的下标从0开始;
  • 2.因此a[2]访问的并不是数组a的第2个元素,而是第三个元素;
  • 3.合理的下标,不能小于0,也不能大于 (数组的长度 - 1);否则就会出现数组下标越界;
(2)数组的大小

数组所占据空间大小的计算遵循下面的简单公式:

数组所占空间 = 每个数据类型所占空间大小 * 元素个数
#include <iostream>
using namespace std;
int main() {
	int a[4] = {4,2,1,5};
	//获取数组的长度
	int len = sizeof(a) / sizeof(a[0]);
	cout<<"数组的长度:"<<len; //4 
}

说明:
//sizeof(a): a所占空间大小
//sizeof(a[0]) : 每个元素所占空间大小

这里为了获取数组的长度,我们使用了sizeof运算符,它可以返回一个数据对象在内存中占用的大小(以字节为单位);数组总大小,除以每个数据元素的大小,就是元素个数。

(3)遍历数组
#include <iostream>
using namespace std;
int main() {
	int a[4] = {4,2,1,5};
	int len = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < len; i++) {
		cout << a[i] << " ";
	}
}

结果:
在这里插入图片描述

4.数组的排序算法
(1)冒泡排序

思路:

  • 1.比较所有相邻的元素,如果第一个比第二个大,则交换他们。
  • 2.一轮下来,可以保证最后一个数是最大的。
  • 3.以此类推,执行n-1轮,就可以完成排序。
  • 动画演示:

在这里插入图片描述
代码参考:

#include <iostream>
using namespace std;
int main() {
	int a[] = {6,5,4,1,3,2};
	int len = sizeof(a) / sizeof(a[0]);
	for(int i = 0; i < len-1; i++) {
		for(int j = 0; j < len-1; j++) {
			if(a[j] > a[j+1]) {
				int temp = a[j];
				a[j] = a[j+1];
				a[j+1] = temp;
			}
		}
	}
	for (int i = 0; i < len; i++) {
		cout << a[i] << " ";
	}
}
  • 思考:以上代码能否进行优化呢?内层循环需要每次都遍历0~len-1次吗??
  • 回答:不需要,因为随着外层循环次数的增加,数组末尾的排序会依次确定好位置,例如,第一轮会确定6的位置,第二轮会确定5的位置。所以,内层循环不需要每次都遍历0~len-1次,只需要遍历len-1-i 次就够了。

优化后的代码:

#include <iostream>
using namespace std;
int main() {
	int a[] = {6,5,4,1,3,2};
	int len = sizeof(a) / sizeof(a[0]);
	for(int i = 0; i < len-1; i++) {
		for(int j = 0; j < len-1-i; j++) {
			if(a[j] > a[j+1]) {
				int temp = a[j];
				a[j] = a[j+1];
				a[j+1] = temp;
			}
		}
	}
	for (int i = 0; i < len; i++) {
		cout << a[i] << " ";
	}
}
(2)选择排序

思路:

  • 1.找到数组中的最小值,把他更换到列表中的第一位。(具体做法:先假设第一数为最小值,记录它的索引值,将第一数和第二个数作比较,如果第一个数大于第二个数,将最小索引值记录为第二个数,依次循环比较,一轮比较下来,最小值所在的索引位置就会被找到,并且把他更换到最开头的位置。
  • 2.接着找到第二小的值,把他更换到数组中的第二位。
  • 3.以此类推,执行n-1轮,就可以完成排序。
  • 动画演示:

在这里插入图片描述

参考代码:

#include <iostream>
using namespace std;
int main() {
	int a[] = {6,5,4,1,3,2};
	int len = sizeof(a) / sizeof(a[0]);
	for(int i = 0; i < len-1; i++) {
		int min = i;
		for(int j = i+1; j < len; j++) {
			if(a[min] > a[j]) {
				min = j;
			}
		}
		int temp = a[min];
		a[min] = a[i];
		a[i] = temp;
	}
	for (int i = 0; i < len; i++) {
		cout << a[i] << " ";
	}
}
(3)插入排序

思路:

  • 从第二个数开始往前比。
  • 如果第二个数比第一个数小,则需要交换他们的位置;再让第三个数和前两个数依次比较(从第二个数开始对比),如果第三个数比其中任何一个数小,则同样需要交换位置。
  • 以此类推,进行到最后一个数。
#include<iostream>
using namespace std;

int main() {
	int a[] = {5,3,4,7,2};
	for(int i = 1; i<5; i++) {
		int key = a[i];
		int j = i-1;
		while(j>=0 && key<a[j]) {
			a[j+1] = a[j];
			j-=1;
		}
		a[j+1] = key;
		for(int z = 0; z<=4; z++) {
			cout<<a[z];
		}
		cout<<endl;
	}

}

5.数组的查找算法
(1)顺序查找

思路:

  • 1.遍历数组。
  • 2.找到跟目标值相等的元素,就输出他的下标。
  • 3.遍历结束后,如果没有搜索到目标值,就输出-1。
#include <iostream>
using namespace std;
int main() {
	int a[5] = {4,5,2,3,1};
	int target = 9;
	int len = sizeof(a)/sizeof(a[0]);
	bool b = false;
	for(int i = 0; i < len; i++) {
		if(target==a[i]) {
			cout<<i<<endl;
			b = true;
		}
	}
	if(b==false) {
		cout<<-1<<endl;
	}
}
(2)二分查找

【注意】:二分查找的前提是数组是排序好的。

思路:

  • 1.从数组的中间元素开始,如果中间元素正好是目标值,则搜索结束。
  • 2.如果目标值大于或者小于中间元素,则在大于或小于中间元素的那一半数组中搜索。
#include <iostream>
using namespace std;
int main() {
	int a[5] = {4,5,8,9,15};
	int target = 5;
	int len = sizeof(a)/sizeof(a[0]);
	int left = 0;
	int right = len-1;
	bool b = false;
	while(left<=right){
		int mid = (left+right)/2;
		if(a[mid]<target){
			left = mid+1;
		}else if(a[mid]>target){
			right = mid-1;
		}else{
			cout<<mid;
			b = true;
			break;
		}
	}
	if(b==false) {
		cout<<"找不到该数字";
	}	
}
6.二维数组
(1)二维数组的初始化
#include<iostream>
using namespace std;

int main() {
	int ia[3][4] = {
		{1,2,3,4},
		{5,6,7,8},
		{9,10,11,12}
	};
}
(2)访问二维数组的大小和元素
#include<iostream>
using namespace std;

int main() {
	int ia[3][4] = {
		{1,2,3,4},
		{5,6,7,8},
		{9,10,11,12}
	};
	// 访问ia的第二行、第三个数据
	cout << "ia[1][2] = " << ia[1][2] << endl;

	cout << "二维数组总大小:" << sizeof(ia) << endl;
	cout << "二维数组每行大小:" << sizeof(ia[0]) << endl;
	cout << "二维数组每个元素大小:" << sizeof(ia[0][0]) << endl;
}

结果:
在这里插入图片描述

(3)遍历二维数组
#include<iostream>
using namespace std;

int main() {
	int ia[3][4] = {
		{1,2,3,4},
		{5,6,7,8},
		{9,10,11,12}
	};

	// 修改ia的第一行、第二个数据
	// ia[0][1] = 19;

	// 二维数组行数
	int rowCnt = sizeof(ia) / sizeof(ia[0]); //3
	// 二维数组列数
	int colCnt = sizeof(ia[0]) / sizeof(ia[0][0]); //4

	// 遍历二维数组
	for (int i = 0; i < rowCnt; i++) {
		for (int j = 0; j < colCnt; j++) {
			cout << ia[i][j] << "\t";
		}
		cout << endl;
	}
}

结果:
在这里插入图片描述

2.字符串

  • C++的标准库中,提供了一种用来表示字符串的数据类型string,这种类型能够表示长度可变的字符序列,使用它必须包含string头文件
#include<string>
(1)字符串的定义
#include<iostream>
#include<string>
using namespace std;

int main() {
	string s3 = "Hello World!";
	cout<<s3<<endl;
}
(2)访问字符串元素
  • 字符串同样是通过索引访问内部的每个字符,索引从0开始。
  • 获取字符串的长度:str.size()
#include<iostream>
#include<string>
using namespace std;

int main() {
	string str = "hello world";
	
	// 获取第5个字符
	cout << "str[4] = " << str[4] << endl;
	
	// 将第1个字符改为'H'
	str[0] = 'H';
	
	// 将最后一个字符改为'D'
	str[str.size() - 1] = 'D';

	cout << "str = " << str << endl;
}

结果:

在这里插入图片描述

(3)遍历字符串
#include<iostream>
#include<string>
using namespace std;

int main() {
	string str = "hello world";

	//	遍历字符串中的元素
	for(int i = 0; i<str.size(); i++) {
		cout<<str[i]<<" ";
	}
}

3.指针

(1)指针基础
  • 指针的定义语法形式为:
类型 * 指针变量;

这里的类型就是指针所指向的数据类型,后面加上星号“*”,然后跟指针变量的名称。例如:

#include<iostream>
using namespace std;
int main(){
	int num = 10;
	int *p; //声明  
	p = &num; //赋值 
	cout<<p<<endl; //输出变量p存储的地址 
	cout<<*p; //输出10 
} 

在这里插入图片描述

  • 流程图解析:
    在这里插入图片描述
  • 总结:指针的本质,其实就是一个数表示的内存地址。
    在这里插入图片描述
  • 再举个例子:
#include<iostream>
using namespace std;
int main(){
	int num = 10;
	int *p; //声明  
	p = &num; //赋值 : 将num的地址赋给指针变量p 
	cout<<p<<endl; //输出变量p存储的地址 
	cout<<*p<<endl; //输出10 
	
	*p = 20;
	cout<<num<<endl; //20
	cout<<*p<<endl; //20
} 
(2)指针用途
  • 简单的指针运算
#include<iostream>
using namespace std;
int main(){
	int num = 10;
	int *p; //声明  
	p = &num; //赋值
	cout<<p<<endl; //输出变量p存储的地址 
	p++;
	cout<<p<<endl; 
} 

在这里插入图片描述

  • 数组指向
#include<iostream>
using namespace std;
int main(){
	int a[] = {1,2,3};
	int *p = a; //声明 
	cout<<p<<endl; //输出变量p存储的地址 
	cout<<*p<<endl; 
	p++;//p++之后,内存地址加4个字节,正好指针会移动到下一个数组元素 
	cout<<*p<<endl;
	
	*p = 200;//通过指针改变数组中的元素 
	cout<<a[1]; 
} 

在这里插入图片描述

  • 遍历数组元素
#include<iostream>
using namespace std;
int main() {

	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	int * p = arr;  //指向数组的指针

	cout << "第一个元素: " << arr[0] << endl;
	cout << "指针访问第一个元素: " << *p << endl;

	for (int i = 0; i < 10; i++) {
		//利用指针遍历数组
		cout << *p << endl;
		p++;
	}
}
  • 可作为函数的参数和返回值
#include<iostream>
using namespace std;

void increment(int* ptr) {
	(*ptr)++;  // 通过指针修改传入的值
}

int main() {
	int x = 10;
	increment(&x);  // 传递x的地址给increment函数
	cout << x << endl;  // 输出11
}
(3) const修饰指针

const修饰指针有三种情况

  1. const修饰指针 — 常量指针
  2. const修饰常量 — 指针常量
  3. const即修饰指针,又修饰常量
#include<iostream>
using namespace std;
int main() {

	int a = 10;
	int b = 10;

	//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
	const int * p1 = &a;
	p1 = &b; //正确
	//*p1 = 100;  报错

	//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
	int * const p2 = &a;
	//p2 = &b; //错误
	*p2 = 100; //正确

    //const既修饰指针又修饰常量
	const int * const p3 = &a;
	//p3 = &b; //错误
	//*p3 = 100; //错误

}
  • 总结:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量
(4)指针和函数详解
#include<iostream>
using namespace std;
//值传递
void swap1(int a ,int b)
{
	int temp = a;
	a = b; 
	b = temp;
}
//地址传递
void swap2(int * p1, int *p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

int main() {

	int a = 10;
	int b = 20;
	swap1(a, b); // 值传递不会改变实参

	swap2(&a, &b); //地址传递会改变实参

	cout << "a = " << a << endl;

	cout << "b = " << b << endl;

}

总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递

(5)实战演练
  • 案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序

  • 例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };

#include<iostream>
using namespace std;
//冒泡排序函数
void bubbleSort(int * arr, int len) { //int * arr 也可以写为int arr[]
	for (int i = 0; i < len - 1; i++) {
		for (int j = 0; j < len - 1 - i; 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++) {
		cout << arr[i] << endl;
	}
}

int main() {

	int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
	int len = sizeof(arr) / sizeof(int);

	bubbleSort(arr, len);

	printArray(arr, len);
}

4.结构体

结构体的特点:一个结构体类型,可以包含多个成员(类似数组元素),每个成员类型不限。可以做到一批不同类型的数据,混装在一个结构体内。

(1)结构体的基本应用:
#include<iostream>
using namespace std;

int main() {
	struct Student{
		string name;//成员1:姓名 
		int age;//成员2:年龄 
		string gender;//成员3:性别 
	}; 
	struct Student stu;//结构体变量 
	stu = {"周杰伦",13,"男"}; 
	
//	cout<<stu<<endl;//结构体变量是一个整体包装,无法直接输出 
	cout<<stu.name<<endl;//周杰伦
	cout<<stu.age<<endl;//13
	cout<<stu.gender<<endl;//男
	
	struct Student stu2;//结构体变量 
	stu2 = {"林俊杰",43,"男"};
	cout<<stu2.name<<endl;//林俊杰
	cout<<stu2.age<<endl;//43
	cout<<stu2.gender<<endl;//男
}
(2)结构体成员的默认值

【注意】:结构体成员的默认值,需要编译器支持C++11或更高版本。

#include<iostream>
using namespace std;
int main() {
	struct Student{
		string name; //成员1:姓名
		string major_code = "003032"; //成员2:专业代码,拥有默认值 003032
		int dormitory_num = 1; //成员3:宿舍楼号,默认值1
	};

	struct Student s1 = {"周杰伦"};
	struct Student s2 = {"林俊杰", "003001", 3};

	cout<<s1.name<<endl;
	cout<<s1.major_code<<endl;
	cout<<s1.dormitory_num<<endl;

	cout<<endl;

	cout<<s2.name<<endl;
	cout<<s2.major_code<<endl;
	cout<<s2.dormitory_num<<endl;
}

在这里插入图片描述

(3)结构体数组
  • 两种写法:

在这里插入图片描述

  • 第一种:
#include<iostream>
using namespace std;

int main() {
	struct Student {
		string name;
		int age;
		string gender;
	};

	struct Student arr[3]; //结构体数组对象的声明
	arr[0] = {"AA", 11, "男"};
	arr[1] = {"BB", 12, "女"};
	arr[2] = {"CC", 13, "男"};

	for (int i=0; i<3; i++) {
		cout << arr[i].name << " ";
		cout << arr[i].age << " ";
		cout << arr[i].gender << " ";
		cout << endl;
	}
}
  • 第二种:
#include<iostream>
using namespace std;

int main() {
	struct Student {
		string name;
		int age;
		string gender;
	};

	struct Student arr[3] = {
		{"AA", 11, "男"},
		{"BB", 12, "女"},
		{"CC", 13, "男"}
	};

	for (int i=0; i<3; i++) {
		cout << arr[i].name << " ";
		cout << arr[i].age << " ";
		cout << arr[i].gender << " ";
		cout << endl;
	}

	return 0;
}

结果:
在这里插入图片描述

(4)结构体指针

在这里插入图片描述

#include "iostream"
using namespace std;

int main()
{
    struct Student
    {
        string name;
        int age;
        string gender;
    };

    // 先创建一个标准的结构体对象(静态内存管理)
    struct Student stu = {"周杰轮", 11, "男"};
    
    
    // 直接通过结构体对象.xxx的方式访问成员属性 
    cout << "结构体中成员的name:" << stu.name << endl;
    cout << "结构体中成员的age:" << stu.age << endl;
    cout << "结构体中成员的gender:" << stu.gender << endl;
    
    // 创建结构体的指针,指向结构体对象的地址
    struct Student * p = &stu;
    
    // 通过结构体指针,访问结构体的成员属性,要使用的符号是:->
    
    cout << "结构体中成员的name:" << p->name << endl;
    cout << "结构体中成员的age:" << p->age << endl;
    cout << "结构体中成员的gender:" << p->gender << endl;


    // 通过new操作符,申请结构体的空间
    struct Student * p2 = new Student {"林军杰", 21, "男"};
    cout << "结构体2中成员的name:" << p2->name << endl;
    cout << "结构体2中成员的age:" << p2->age << endl;
    cout << "结构体2中成员的gender:" << p2->gender << endl;

    delete p2;
}

在这里插入图片描述

  • 总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员
(5)结构体指针数组

在这里插入图片描述

#include "iostream"
using namespace std;

int main()
{
    struct Student
    {
        string name;
        int age;
        string gender;
    };
    struct Student arr1[3] = {{"周杰轮"}, {"林军杰"}, {"王力宏"}};
    struct Student * p1 = arr1;     // 数组的对象本质上就是地址(指向数组的第一个元素的位置)
    cout << "数组的第一个元素中记录的name是:" << p1[0].name << endl;
    cout << "数组的第二个元素中记录的name是:" << p1[1].name << endl;
    cout << "数组的第三个元素中记录的name是:" << p1[2].name << endl;

//     通过new操作符,自行申请结构体数组的空间(可以通过delete回收)
    struct Student * p2 =
            new Student[3] {{"周杰轮"}, {"林军杰"}, {"王力鸿"}};

    cout << "数组2的第一个元素中记录的name是:" << p2[0].name << endl;
    cout << "数组2的第二个元素中记录的name是:" << p2[1].name << endl;
    cout << "数组2的第三个元素中记录的name是:" << p2[2].name << endl;

    delete[] p2;

}

在这里插入图片描述

(6)结构体嵌套结构体
  • 作用: 结构体中的成员可以是另一个结构体

  • 例如:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体

示例:

#include "iostream"
using namespace std;
//学生结构体定义
struct student {
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//教师结构体定义
struct teacher {
	//成员列表
	int id; //职工编号
	string name;  //教师姓名
	int age;   //教师年龄
	struct student stu; //子结构体 学生
};

int main() {

	struct teacher t1;
	t1.id = 10000;
	t1.name = "老王";
	t1.age = 40;

	t1.stu.name = "张三";
	t1.stu.age = 18;
	t1.stu.score = 100;

	cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;

	cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl;
}

在这里插入图片描述

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

  • 传递方式有两种:1、值传递 2、 地址传递

示例:

#include "iostream"
using namespace std;
//学生结构体定义
struct student {
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//值传递
void printStudent(student stu ) {
	stu.age = 28;
	cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;
}

//地址传递
void printStudent2(student *stu) {
	stu->age = 28;
	cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age  << " 分数:" << stu->score << endl;
}

int main() {

	student stu = { "张三",18,100};
	//值传递
	printStudent(stu);
	cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;

	cout << endl;

	//地址传递
	printStudent2(&stu);
	cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;
}
  • 总结:如果不想修改主函数中的数据,用值传递,反之用地址传递
(8)实战演练

案例描述:

  • 设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。

  • 通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。

五名英雄信息如下:

{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"},
#include "iostream"
using namespace std;
//英雄结构体
struct hero {
	string name;
	int age;
	string sex;
};
//冒泡排序
void bubbleSort(hero arr[] , int len) {
	for (int i = 0; i < len - 1; i++) {
		for (int j = 0; j < len - 1 - i; j++) {
			if (arr[j].age > arr[j + 1].age) {
				hero temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
//打印数组
void printHeros(hero arr[], int len) {
	for (int i = 0; i < len; i++) {
		cout << "姓名: " << arr[i].name << " 性别: " << arr[i].sex << " 年龄: " << arr[i].age << endl;
	}
}

int main() {

	struct hero arr[5] = {
		{"刘备",23,"男"},
		{"关羽",22,"男"},
		{"张飞",20,"男"},
		{"赵云",21,"男"},
		{"貂蝉",19,"女"},
	};

	int len = sizeof(arr) / sizeof(hero); //获取数组元素个数

	bubbleSort(arr, len); //排序

	printHeros(arr, len); //打印
}

在这里插入图片描述

5.模板类vector

(1)简介
  • “容器”vector是对数组功能进行扩展的一个标准库类型。顾名思义,vector“容纳”着一堆数据对象,其实就是一组类型相同的数据对象的集合。

  • 注意:vector是标准库的一部分。要想使用vector,必须在程序中包含头文件,并使用std命名空间。

(2)用法
  • 在vector后面跟一个尖括号<>,里面填入具体类型信息。
#include <iostream>
#include <vector>
using namespace std;

int main() {
	// 默认初始化,不含任何元素
	vector<int> v1;
	
	// 列表初始化
	vector<char> v2 = {'a', 'b', 'c'};
	
	// 省略等号的列表初始化
	vector<short> v3{1,2,3,4,5};

	// 只定义长度,元素初值默认初始化,容器中有5个0
	vector<int> v4(5);
	
	// 定义长度和初始值,容器中有5个100
	vector<long> v5(5, 100);
	for(int i = 0;i<v5.size();i++){
		cout<<v5[i]<<endl;
	}
	
	//	第二种遍历的方式: 
//	for (int num: v5)
//	{
//		cout << num << "\t";
//	}

//	【添加元素】 :vector的长度并不是固定的,所以可以向一个定义好的vector添加元素。 
	v5.push_back(69);
	for (int num : v5)
	{
		cout << num << "\t"; //100     100     100     100     100     69 
	}	
}

小试牛刀:

题目:使用vector,往数组中添加10—1倒序的元素,并打印出来

#include <iostream>
#include <vector>
using namespace std;

int main() {
	vector<int> vec;

	for (int i = 10; i > 0; i--) {
		vec.push_back(i);
	}
	
	for (int i = 0;i<vec.size();i++) {
		cout<<vec[i]<<"\t";
	}
}
(3)vector和数组的区别
  • 数组是更加底层的数据类型;长度固定,功能较少,安全性没有保证;但性能更好,运行更高效;
  • vector是模板类,是数组的上层抽象;长度不定,功能强大;缺点是运行效率较低;
  • 除了vector之外,C++ 11 还新增了一个array模板类,它跟数组更加类似,长度是固定的,但更加方便、更加安全。所以在实际应用中,一般推荐对于固定长度的数组使用array,不固定长度的数组使用vector。

十二、函数

1.函数基本知识

(1)函数定义

一个完整的函数定义主要包括以下部分:

  • 返回类型:调用函数之后,返回结果的数据类型;
  • 函数名:用来命名代码块的标识符,在当前作用域内唯一;
  • 参数列表:参数表示函数调用时需要传入的数据,一般叫做“形参”;放在函数名后的小括号里,可以有0个或多个,用逗号隔开;
  • 函数体:函数要执行的语句块,用花括号括起来。

函数一般都是一个实现了固定功能的模块,把参数看成“输入”,返回结果看成“输出”,函数就是一个输入到输出的映射关系。

我们可以定义一个非常简单的平方函数:

// 平方函数 y = f(x) = x ^ 2
int square(int x)
{
	int y = x * x;
	return y;
}

使用流程控制语句return,就可以返回结果。

(2)函数调用
  • 调用函数时,使用的是“调用运算符”,就是跟在函数名后面的一对小括号;括号内是用逗号隔开的参数列表。
  • 这里的参数不是定义时的形参,而是为了初始化形参传入的具体值;为了跟函数定义时的形参列表区分,把它叫作“实参”。
  • 调用表达式的类型就是函数的返回类型,值就是函数执行返回的结果。
#include<iostream>
using namespace std;

// 平方函数 y = f(x^2)
int square(int x)
{
	return x * x;
}

int main()
{
	int n = 6;
	cout << n << "的平方是:" << square(n) << endl; 
}

【注意】:

  • 实参是形参的初始值,所以函数调用时传入实参,相当于执行了int x = 6的初始化操作;实参的类型必须跟形参类型匹配;
  • 实参的个数必须跟形参一致;如果有多个形参,要按照位置顺序一一对应;
  • 如果函数本身没有参数,参数列表可以为空,但空括号不能省;
  • 形参列表中多个参数用逗号分隔,每个都要带上类型,类型相同也不能省略;
  • 如果函数不需要返回值,可以定义返回类型为void;
  • 函数返回类型不能是数组或者函数。
(3)全局变量和局部变量

在C++中,全局变量和局部变量是两种不同类型的变量,它们在多个方面存在显著的差异。以下是它们之间的主要区别:

(1)作用域:

  • 全局变量:在函数之外定义,其作用域从定义点开始,直到程序结束。全局变量可以在整个程序中访问,包括所有的函数和块。
  • 局部变量:在函数或代码块内部定义,其作用域仅限于定义它的函数或代码块。一旦离开该函数或代码块,局部变量就不再存在。

(2)生命周期:

  • 全局变量:在程序开始执行时创建,并在程序结束时销毁。它们的生命周期是整个程序的执行时间。
  • 局部变量:在函数或代码块被调用时创建,当函数或代码块执行完毕后销毁。它们的生命周期是短暂的,仅限于函数或代码块的执行期间。
#include <iostream>
using namespace std;

// 全局变量
int globalVar = 100;

int fn() {
	// 局部变量
	int num = 300;
	cout << "局部变量 num 的值是: " << num << endl;
	
	// 访问全局变量
	cout << "全局变量 globalVar 的值是: " << globalVar << endl;
	
	//	cout << localVar << endl; //编译错误 
}

int main() {	
	// 局部变量
	int localVar = 200;
	cout << "局部变量 localVar 的值是: " << localVar << endl;
	
	// 访问全局变量
	cout << "全局变量 globalVar 的值是: " << globalVar << endl;

	fn();
}
(4)函数声明
  • 如果我们将一个函数放在主函数后面,就会出现运行错误:找不到标识符。这是因为函数和变量一样,使用之前必须要做声明。函数只有一个定义,可以定义在任何地方;如果需要调用函数,只需要在调用前做一个声明,告诉编译器“存在这个函数”就可以了。
  • 函数声明的方式,和函数的定义非常相似;区别在于声明时不需要把函数体写出来,用一个分号替代就可以了。
#include <iostream>
using namespace std;
// 函数声明  
void myFunction();
  
int main() {  
    myFunction(); // 可以调用,尽管定义在后面,但是函数已经提前声明过了 
    return 0;  
}  
  
// 函数定义  
void myFunction() {  
    // 函数体  
    cout<<666; 
}

2.参数传递

#include<iostream>
using namespace std;

int square(int x) {
	return x * x;
}
int main() {
	int n = 6;
	cout << n << "的平方是:" << square(n) << endl;
}

在上面平方函数的调用中,实参n的值 6 被拷贝给了形参x。

(1)传值的困扰
#include<iostream>
using namespace std;

void increase(int x) {
	++x;
}
int main() {
	int n = 6;
	increase(n); // n的值不会增加
	cout<<n; //6
}
(2)指针形参
  • 使用指针形参可以解决以上传值的这个问题。如果我们把指向数据对象的指针作为形参,那么初始化时拷贝的就是指针的值;复制之后的指针,依然指向原始数据对象,这样就可以保留它的更改了。
#include<iostream>
using namespace std;

// 指针形参
void increase(int *p) {
	++(*p);
}
int main() {
	int n = 0;
	increase(&n); // 传入n的地址,调用函数后n的值会加1
	cout<<n; //1
}

3.返回类型

(1)无返回值
  • 当函数返回类型为void时,表示函数没有返回值。可以在函数中需要返回时直接执行 return语句,也可以不写。因为返回类型为void的函数执行完最后一句,会自动加上return返回。
  • 例如,可以将之前“两元素值互换”的代码,包装成一个函数。可以先做一个判断,如果两者相等就直接返回,这样可以提高运行效率。
// 元素互换
void swap(int& x, int& y)
{
	if (x == y)
		return;    // 不需要交换,直接返回

	int temp = x;
	x = y;
	y = temp;
}
(2)有返回值

如果函数返回类型不为void,那么函数必须执行return,并且每条return必须返回一个值。返回值的类型应该跟函数返回类型一致,或者可以隐式转换为一致。

#include<iostream>
using namespace std;

// 字符串比较长度,返回较长的
string longerStr(const string& str1, const string& str2) {
	return str1.size() > str2.size() ? str1 : str2;
}
int main() {
	string str1 = "hello world!", str2 = "c++ is interesting!";
	cout << longerStr(str1, str2) << endl;
}

4.递归

  • 如果一个函数调用了自身,这样的函数就叫做“递归函数”。

  • 递归是调用自身,如果不加限制,这个过程是不会结束的;函数永远调用自己下去,最终会导致程序栈空间耗尽。所以在递归函数中,一定会有某种“基准情况”,这个时候不会调用自身,而是直接返回结果。

(1)用递归方法求1+2+…+N的值
#include<iostream>
using namespace std;

int fn(int n) {
	if (n == 1) {
		return 1;
	} else {
		return fn(n - 1) + n;
	}
}

int main() {
	int n;
	cin>>n;
	cout << "结果:" << fn(n) << endl;
}
(2)求5的阶乘
#include<iostream>
using namespace std;

// 递归方式求阶乘
int factorial(int n)
{
	if (n == 1)
		return 1;
	else
		return factorial(n - 1) * n;
}

int main()
{
	cout << "5! = " << factorial(5) << endl;
}
  • 递归示意图:
    在这里插入图片描述
(3)斐波那契数列
  • 斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:1, 1, 2, 3, 5, 8, 13, 21, 34, …
  • 它的规律是:当前数字,是之前两个数字之和。在数学上,斐波那契数列被以递推的方法定义: F(0)=1,F(1)=1, F(n) = F(n - 1) + F(n - 2)(n ≥ 2)
#include<iostream>
using namespace std;

int fib(int n) {
	if (n == 1 || n == 2)
		return 1;
	return fib(n - 2) + fib(n - 1);
}

int main() {
	cout << "fib(7) = " << fib(7) << endl;
}

(4)真题演练

在这里插入图片描述
在这里插入图片描述

  • 第一种解法:递归
#include<iostream>
using namespace std;

int fn(int n) {
	if (n == 1) {
		return 1;
	} else {
		return fn(n - 1) + n;
	}
}

int main() {
	int n;
	cin>>n;
	int sum = 0;
	for(int i = 1; i<=n; i++) {
		sum += fn(i);
	}
	cout << sum << endl;
}
  • 第二种解法:for循环嵌套
#include<iostream>
using namespace std;

int main() {
	int n;
	cin>>n;
	int sum = 0;
	int result = 0;
	for(int i = 1; i<=n; i++) {
		sum += i;
		result+=sum;
	}
	cout << result << endl;
}
  • 第三种解法:等差数列求和公式

在这里插入图片描述

#include <iostream>
using namespace std;
int main() {
	int n = 0;
	cin >> n;
	int sum = 0;
	for (int i = 1; i <= n; i++){
		sum += (1 + i) * i / 2;
	}
	cout << sum << endl;
}
(4)路飞吃桃

题目链接

参考代码:递归

#include <iostream>
using namespace std;
int fn(int n) {
	if (n==1) {
		return 1;
	}
	return (fn(n-1)+1)*2;
}

int main() {
	int n;
	cin>>n;
	cout<<fn(n);
}

参考代码:for循环

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n; // 输入天数 n

    int peaches = 1; // 第 n 天剩下的桃子数量

    // 逆向计算桃子的数量
    for (int day = n; day > 1; --day) {
        // 在第 day 天开始前, 路飞有的桃子数量是 (peaches + 1) * 2
        peaches = (peaches + 1) * 2;
    }

    cout << peaches << endl; // 输出最初桃子的数量

    return 0;
}
(5)弹簧板

题目链接

参考代码:

#include <iostream>
#include <vector>
using namespace std;
int fn(int i,vector<int> &arr,int n) {
	if(i>=n) return 0;
	return fn(i+arr[i],arr,n)+1;
}

int main() {
	int n;
	cin>>n;
	vector<int> arr;
	for(int i = 0; i<n; i++) {
		int a;
		cin>>a;
		arr.push_back(a);
	}
	cout<<fn(0,arr,n)<<endl;
}
(6)递归实现指数型枚举

题目链接

#include <iostream>
#include <vector>
using namespace std;

int arr[10];
void result(int n) {
	for(int i = 0; i<=n; i++) {
		if(i) cout<<" ";
		cout<<arr[i];
	}
	cout<<endl;
}

void fn(int i,int j,int n) {
	if (j>n) return;
	for(int k = j; k<=n; k++) {
		arr[i] = k;
		result(i);
		fn(i+1,k+1,n);
	}
	return;
}

int main() {
	int n;
	cin>>n;
	fn(0,1,n);
}
(7)递归实现组合型枚举

题目链接


(8)递归实现排列型枚举

题目链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值