C++教程
应用方向
网络通信、嵌入式、数据库、音视频、游戏、桌面、人工智能、大数据等
明确 C++课程学习阶段以及效果
阶段 | 内容 | 目标 |
---|---|---|
第一阶段 | 基础语法 | 对 C++有初步了解,能够有基础编程能力 |
第二阶段 | 核心编程 | 介绍 C++面向对象编程,为大型项目做铺垫 |
第三阶段 | 高级编程 | 介绍 C++泛型编程思想,以及 STL 的基本使用 |
c++编写程序步骤
- 创建项目
- 创建文件
- 编写代码
- 打包编译
- 运行程序
环境设置
确保电脑有 c++环境,确保电脑上有文本编辑器和 C++编译器。
文本编辑器
通过文本编辑器创建的文件通常称为源文件,源文件里是 程序的源代码,c++程序的源文件通常使用扩展名.cpp
、.cp
、或.c
。
文本编辑器可以使用Visual Studio Code
,它是微软公司用 Electron 开发的,它支持 c/c++的开发。有些程序员喜欢用Visual Studio
。这个面向.NET 和 C++开发人员的综合性 Windows 版 IDE。
C++编译器
源文件中源代码是人类可以直接读的,它需要被编译成机器语言(二进制),这样 CPU 就可以执行它。C++编译器用于将源代码编译成机器语言,生成可执行的文件。
大多数的 C++编译器不在乎源文件的扩展名,但是如果开发者(程序员)没有指定扩展名,默认是.cpp
。最多使用的是 GNU 的 C/C++编译器。
变量
功能:给一段指定的内存空间起名字,方便操作这个内存空间。
命名规则
不能使用关键字,只能用字母、数字、下划线组合。第一个字符必须是字母或下划线。
语法
数据类型
变量名
= 初始值
;
int a = 10;
#include<iostream>
using namespace std;
int main(){
int a = 10;
cout << "a=" << a << endl;
system("pause");
return 0;
}
常量
功能:常量是指在程序运行过程中值不可被改变的变量。定义常量有两种方式
语法
使用const
关键字,这时 C++中声明常量最常用的方法,在变量声明符之前加上const
即可。
const int a = 10;
使用宏定义,可以使用预处理器宏定义来声明常量。
#define PI 3.14159265
注释
功能:在代码中加上一些说明和解释,方便程序员阅读代码。
单行注释
//描述信息
多行注释
/*描述信息*/
关键字
功能:关键字是 c++中预先保留的单词,定义变量或者常量不要使用关键字。
关键字 | 对应中文 | 功能说明 |
---|---|---|
auto | 自动 | 自动推导变量或表达式的类型 |
break | 终止 | 用于提前退出循环或 switch 语句 |
case | 情况 | 用于定义 switch 语句中的特定分支 |
catch | 捕获 | 用于处理异常块中的异常 |
class | 类 | 用于定义创建对象的类模版 |
const | 常量 | 声明一个常量变量或函数,其值在初始化后不能更改 |
continue | 继续 | 用于跳过当前循环,继续下一次循环。 |
delete | 删除 | 用于释放内存,并销毁对象实例 |
do | 执行 | 与结合使用while 来创建do-while 循环 |
double | 双精度浮点数 | 表示双精度浮点数(更高精度的小数) |
else | 否则 | 用于指定条件为假时的替代执行路径 |
enum | 枚举 | 用于定义具有一组命名常量的用户定义类型 |
extern | 外部 | 声明在另一个文件夹中定义的变量或函数 |
false | 假 | 代表布尔值 false |
float | 浮点数 | 表示浮点数(十进制数) |
for | 循环 | 用于具有预先定义循环结构的迭代执行 |
friend | 友好函数 | 将非成员函数或类声明为类的友元,允许访问其私有成员 |
goto | 跳转 | 用于将控制转移到程序中的特定标记语句 |
if | 如果 | 用于基于布尔表达式的条件执行 |
inline | 内联 | 建议编译器内联函数,减少函数调用开销 |
int | 整数 | 表示整数(整数) |
long | 长整数 | 表示长整数(更大范围的整数) |
mutable | 可变 | 声明一个可以在特定范围内修改的常量变量 |
namespace | 命名空间 | 用于定义命名空间,即标识符的逻辑分组 |
new | 新建 | 用于分配内存和创建对象实例 |
null | 空 | 表示空指针,表示不存在有效的对象引用 |
operator | 运算符 | 定义或重新定义自定义操作的运算符 |
private | 私有 | 指定对成员的受保护访问,将其可见行限制在类或结构内 |
protected | 保护 | 指定对成员的受保护访问,允许在类或结构及其派生类中进行访问 |
public | 公共 | 指定对类或结构的成员(变量和函数)的公共访问 |
register | 寄存器 | 建议编译器将变量存储在寄存器中以便更快地访问 |
return | 返回 | 用于退出函数并可选择返回一个值 |
short | 短整数 | 表示短整数(较小范围的整数) |
signed | 有符号 | 表示整型变量可以存储正值和负值 |
sizeof | 大小 | 返回数据类型或变量的大小(以字节为单位) |
static | 静态 | 声明静态变量或函数,其范围仅限于定义它的文件 |
struct | 结构体 | 类似于class ,但通常用于轻量级数据结构 |
switch | 开关 | 用于基于比较表达式的多分支条件执行 |
template | 模板 | 用于定义函数、类或其他构造的通用模板 |
this | 指针 | 引用成员函数中的当前对象 |
throw | 抛出 | 用于引发异常,发生错误或意外情况的信号 |
true | 真 | 代表布尔值 true |
try | 尝试 | 用于定义可能正常运行的代码块。 |
数据类型
c++规定创建变量或常量的时候,必须要指定对应的数据类型。否则无法给变来功能分配内存空间。
整型
整型变量表示的是整数的数据,区别是所占内存空间的不同。
类型 | 说明 | 内存占用空间 | 取值范围 |
---|---|---|---|
short | 短整型 | 2 字节 | (-2^15~2^15-1) |
int | 整型 | 4 字节 | (-2^31~2^31-1) |
long | 长整型 | windows 是 4 字节,linux32 位是 4 字节,linux64 位是 8 字节 | (-2^31~2^31-1) |
long long | 长长整型 | 8 字节 | (-2^63~2^63-1) |
浮点型
C++提供三种浮点数据类型
类型 | 大小 | 近似精度 | 常见用途 |
---|---|---|---|
float | 4 | 7 位小数 | 通用浮点数计算 |
double | 8 | 15 位小数 | 计算要求高精度要求高 |
long double | 16 | 19 位小数 | 极其精确的计算,科学应用 |
对于通用计算float
就足够,如果处理更高精度数字用double
。更苛刻精确的需求,那就用long double
。
字符型
C++提供两种字符数据类型
类型 | 大小 | 常见用途 |
---|---|---|
char | 1 个字节 | 单个字符信息 |
wchar_t | 2 个字节 | 单个字符信息 |
功能:字符型变来那个用于显示单个字符信息,c++用单引号包裹单个字符信息。字符型变来功能并不是把对应的字符本身放在内存中存储,而是将字符本身对应的 ASCII 码放到内存的存储单元中。
字符串型
功能:表示一串字符信息,字符串要加上双引号""
,才不会被警告。
使用字符串赋值
std::string str1 = "HELLO WORLD";
使用字符串连接
std::string str1 = "HELLO";
std::string str2 = "WORLD";
std::string str3 = str1 + str2;
常量字符串赋值
const std:string str1 = "HELLO WORLD";
动态字符串分配
std::string
对象自动为底层字符数组分配和管理内存。开发者不需要手动分配和释放内存。说明:字符串文字会自动转换为std::string
对象。 std::string
支持各种操作和访问字符串内容的方法,对于大多数字符串的操作,使用std::string
而不是原始字符数组。
⚠️ 如何省略std:string
#include <iostream>
// #include"head.h"
using namespace std; //加上这一行
int main()
{
string a = "hello";
cout << a << endl;
return 0;
}
在 C++中,如果你使用了using namespace std;
语句,那么你就可以省略std::
前缀。这个语句会告诉编译器你在使用标准库中的所有符号,所以你就不需要每次都写std::
了。但是,要注意这种做法可能会导致命名冲突或者不明确的情况,特别是在大型项目中,因此并不是一种推荐的做法。
获取字符串长度
使用length()
方法获取字符串长度
std::string str1 = "hello";
int length = str1.length();
访问字符
使用at()
方法访问字符串中的单个字符
std::string str1 = "hello";
char ch = str1[0]; //访问第一个字符
char ch2 = str1[1]; //访问第二个字符
字符串比较
使用==
、!=
等运算符比较两个字符串是否相等
std::string str1 = "hello";
std::string str2 = "hello";
if(str1 == str2){
//字符串相等
}else{
//字符串不想等
}
查找子字符串
使用find()
方法来查找一个子字符串在主字符串中的位置
std::string str1 = "hello world";
size_t pos = str1.find("hello");
替换子字符串
使用replace()
方法来替换字符串中的子字符串
std: string str1 = "hello world";
str.replace(6,5, "everyone")
// 替换后的字符串为 "hello everyone"
子字符串提取
使用substr()
方法从字符串中提取子字符串
std::string str = "Hello, world";
std::string sub = str.substr(7, 5); // 从位置7开始,提取长度为5的子字符串
字符串插入
使用insert()
方法在字符串中插入其他字符串
std::string str = "Hello";
str.insert(5, " world"); // 在位置5处插入字符串" world"
删除子字符串
使用erase()
方法删除字符串中的部分内容
std::string str = "Hello, world";
str.erase(7, 5); // 从位置7开始,删除长度为5的子字符串
字符串大小写转换
使用tolower()
和toupper()
方法将字符串转换为小写或者大写
std::string str = "Hello, World";
for (char &c : str) {
c = std::tolower(c); // 转换为小写
}
字符串比较
使用compare
方法,进行字符串比较
std::string str1 = "hello";
std::string str2 = "HELLO";
int result = str1.compare(str2); // 比较两个字符串(区分大小写)
int resultIgnoreCase = str1.compare(str2, 0, str2.length(), std::string::npos, std::string::npos, true); // 忽略大小写
字符串分割
c++标准库没有提供字符串分割的方法,可以使用标准库中的流操作符和算法实现。
#include <iostream>
#include <sstream>
#include <vector>
std::vector<std::string> split(const std::string &str, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(str);
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
int main() {
std::string str = "apple,banana,cherry";
std::vector<std::string> parts = split(str, ',');
for (const auto &part : parts) {
std::cout << part << std::endl;
}
return 0;
}
布尔型
功能:布尔型代表真或者假的值,true
表示真,false
表示假。
bool 类型占一个字节大小
int main(){
bool flag = true;
return 0;
}
数据输入
功能:用于从键盘获取输入数据
关键字:cin
int main(){
//整型输入
int a = 0;
cout << "请输入整型数字" << endl;
cin >> a;
cout << a << endl;
}
运算符
功能:用于执行代码的计算
算数运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
+ | 正号 | +3 | 3 |
- | 负号 | -3 | -3 |
+ | 加号 | 10+5 | 15 |
- | 减号 | 10-5 | 5 |
* | 乘号 | 10*5 | 50 |
/ | 除号 | 10/5 | 2 |
% | 取模(取余) | 10%3 | 1 |
++ | 前置递增 | a=2; b= ++a; | a=3; b=3; |
++ | 后置递增 | a=2;b=a++; | a=3;b=2; |
-- | 前置递减 | a=2;b=--a; | a=1;b=1; |
-- | 后置递减 | a=2;b=a--; | a=1;b=2; |
nt 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 << a1 / b1 << endl;
int a3 = 10;
int b3 = 0;
//cout << a1 / b1 << endl; // 报错,除数不可以为0
//两个小数可以相除
double d1 = 0.5;
double d2 = 0.25;
cout << d1 / d2 << endl;
system("pause");
return 0;
}
int main(){
int a1 = 10;
int a2 = 3;
// 取模运算本质,就是取余数
cout << a1 % b1 << endl;
int a2 = 10;
int b2 = 20;
cout << a2 % a1 <<endl;
int a3 = 10;
int b3 = 0;
//cout << a3 % b3 << endl; // 报错,取余不可以为0
double d1 = 3.14;
double d2 = 1.1;
cout << d1 % d2 << endl; //报错, 两个小数不可以取模运算
system("pause");
return 0;
}
int main(){
//前置递增
int a = 10;
++a; // 让变量进行+1
cout << "a=" << a << endl;
//后置递增
int b = 10;
b++;
cout << "b=" << b << endl;
// 前置递增和后置递增的区别
//前置递增,先让变量+1,然后进行表达式运算
int a2 = 10;
int b2 = ++a2 * 10;
cout << "a2=" << a2 << endl;
cout << "b2=" << b2 << endl;
//后置递增,先进行表达式运算,后让变量+1
int a3 = 10;
int b3 = a3++ * 10;
cout << "a3=" << a3 << endl;
cout << "b3=" << b3 << endl;
system("pause");
return 0;
}
赋值运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
= | 赋值 | a=2; b=3; | a=2;b=3; |
+= | 加等于 | a=0;a+=2; | a=2; |
-= | 减等于 | a=5; a-=3; | a=2; |
*= | 乘等于 | a=2;a*=2; | a=4; |
/= | 乘等于 | a=4;a/2; | a=2; |
%= | 模等于 | a=3;a%2; | a=1; |
int main(){
//赋值运算符
// =
int a = 10;
a = 100;
cout "a=" << a << endl;
// +=
a = 10;
a += 2;
cout "a=" << a << endl; // 12
// -=
a = 10;
a -= 2;
cout "a=" <<a << endl; //8
// *=
a = 10;
a *= 2;
cout "a=" << a << endl; //20
// /=
a = 10;
a /= 2;
cout "a=" << a << endl; //5
// %=
a = 10;
a %= 2;
cout "a=" << a << endl; //0
system("pause");
return 0;
}
比较运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
== | 相等于 | 4 == 3 | 0 |
!= | 不等于 | 4 != 3 | 1 |
< | 小于 | 4 < 3 | 0 |
> | 大于 | 4 > 3 | 1 |
<= | 小于等于 | 4 <= 3 | 0 |
>= | 大于等于 | 4 >= 1 | 1 |
int main(){
//赋值运算符
// =
int a = 10;
int b = 30;
cout << a == b << endl;
cout << a != b << endl;
cout << a < b << endl;
cout << a > b << endl;
cout << a <= b << endl;
cout << a >= b << endl;
system("pause");
return 0;
}
逻辑运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
! | 非 | !a | 如果 a 为假,则!a 为真, 如果 a 为真,则!a 为假 |
&& | 与 | a && b | 如果 a 和 b 都为真,则结果为真,否则为假 |
|| | 或 | a || b | 如果 a 和 b 有一个为真,则结果为真,二者都为假时,结果为假 |
int main(){
//逻辑运算符 非
//真变假,假变真
int a = 10;
cout << !a << endl; //0
cout << !!a << endl; //1
system("pause");
return 0;
}
int main(){
//逻辑运算符 与
// 同真为真,其余为假
int a = 10;
int b = 10;
cout << a && b << endl; //1
system("pause");
return 0;
}
int main(){
//逻辑运算符 或
int a = 10;
int b = 10;
cout << a || b << endl; //1
system("pause");
return 0;
}
程序流程结构
c++支持最基本的三种程序运行结构
- 顺序结构:程序按照顺序执行,不发生跳转。
- 选择结构:程序按照条件是否满足,有选择的执行相关代码。
- 循环结构:程序按照条件是否满足,循环多次执行相关代码。
选择结构
if 语句
功能:判断表达式或值是否成立或者存在,执行满足条件的语句
三种形式:
- 单行格式 if 语句
- 多行格式 if 语句
- 多条件 if 语句
单行格式 if 语句
int main(){
int score = 0;
cout << "请输入分数" << endl;
cin >> score;
cout << "你输入的分数是:" << score << endl;
if(score > 600){
cout << "🎉恭喜你考上一本了" << endl;
}
system("pause");
return 0;
}
多行格式 if 语句
int main(){
int score = 0;
cout << "请输入分数" << endl;
cin >> score;
cout << "你输入的分数是:" << score << endl;
if(score > 600){
cout << "🎉恭喜你考上一本了" << endl;
}else{
cout << 😔"很遗憾你没考上一本" << endl;
}
system("pause");
return 0;
}
多条件的 if 语句
int main(){
//用户输入分数,如果分数大于100, 视为考上一本,在屏幕输出
//1.用户输入分数
int score = 0;
cout << "请输入分数" << endl;
cin >> souce;
//2.打印用户输入的分数
cout << "你输入的分数是:"<< score << endl;
//3.判断分数是否大于600, 如果大于输出
if(score > 600){
cout << "恭喜你考上一本" << endl;
}
else if(scoure > 500){
cout << "恭喜你考上二本" << endl;
}
else if(scoure > 400){
cout << "恭喜你考上三本" << endl;
}
else{
cout << "再接再厉" << endl;
}
system("pause");
return 0;
}
嵌套 if 语句
功能:if 语句里可以嵌套使用 if 语句,达到更精确的条件判断。
案例需求:
- 提示用户输入一个高考考试分数,根据分数做如下判断
- 分数如果大于 600 分视为考上一本,大于 500 分考上二本,大于 400 考上三本,其余视为未考上本科
- 在一本分数中,如果大于 700 分,考上北大,大于 650 分,考入清华,大于 600 分考入人大。
int main(){
int score = 0;
cout << "请输入考试分数:" << endl;
cin >> score;
if(score > 600){
if(score >= 650){
cout << "考上清华" << endl;
}
if(score >= 700){
cout << "考上北大" << endl;
}
cout << "考上人大" << endl;
}
}
三只小猪比体重
解题思路:先判断 A 和 B 谁重,如果 A 重,让 A 和 C 比体重,如果 B 重,让 B 和 C 比体重
int main(){
int a = 0;
int b = 0;
int c = 0;
cout << "请输入A小猪的体重" << endl;
cin >> a;
cout << "请输入B小猪的体重" << endl;
cin >> b;
cout << "请输入C小猪的体重" << endl;
cin >> c;
//判断哪只最重
//1.先判断A和B
if(a > b){
if(a > c){
cout << "A小猪重" << endl;
}
else{
cout << "C小猪重" << endl;
}
}
else{
if(b > c){
cout << "B小猪重" << endl;
}
else{
cout << "C小猪重" << endl;
}
}
system("pause");
return 0;
}
三木运算符
功能:实现简单的判断
语法:表达式1 ? 表达式2 : 表达式3
解释:如果表达式 1 是真,执行表达式 2。如果表达式 1 是假,执行表达式 3。
int main(){
int a = 10;
int b = 20;
int 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;
system("pause");
return 0;
}
switch 语句
功能:执行多条件分支语句
语法:
switch(表达式)
{
case 结果1:执行语句;break;
case 结果2:执行语句;break;
...
default:执行语句;break;
}
int main(){
int score = 0;
//1.提示用户打分
cout << "请打分" << endl;
cin >> score;
cout << "你大的分数" << score endl;
switch(score){
case 10:
cout << "经典" << endl;
break;
case 9:
cout << "平常" << endl;
break;
default:
cout << "1" << endl;
break;
}
system("pause");
return 0;
}
switch
更适合于有固定数量的离散选项的情况,而if-else
更灵活,适用于更广泛的条件判断。switch
语句可能会因为缺少break
语句而导致 Fall Through,这有时是不希望发生的,而if-else
则不会有这个问题。- 在性能上,
switch
和if-else
通常没有显著差异,但在某些情况下,编译器可能会对switch
语句进行优化,使其在处理固定选项时效率更高。
循环语句
while
功能:满足循环条件,执行循环语句
语法:
while(循环条件){
循环语句
}
解释:只要循环条件为真,就执行循环语句。
int main(){
int num = 0;
while (num < 10){
cout << num << endl;
num++;
}
system("pause");
return 0;
}
注意:在执行循环语句的时候,代码中必须提供跳出循环的条件(出口),否则这个循环就是死循环。
猜数字
int main(){
//系统生成随机数
int num = rand()%100 +1
//玩家进行猜测
int val = 0;
cin >> val;
if (val > num){
cout << "大了" << endl;
}
if(val < num){
cout << "小了" << endl;
}
else{
cout << "猜对了" << endl;
break; //利用break退出循环
}
//判断玩家的猜测
//猜对退出游戏
//猜错继续
system("pause");
return 0;
}
do while
功能:满足循环条件,执行循环语句
语法:
do{循环语句} while(循环条件);
解释:先执行循环语句,再判断循环条件。
int main(){
int num = 0;
do{
cout << num << endl;
num ++;
}while(num < 10);
system("pause");
return 0;
}
注意:跟 while 的区别是 do while 会先执行一次循环语句,再判断循环条件。
水仙花数
int main(){
//先打印所有的三位数字
int num = 100;
do{
int a = 0;
int b = 0;
int c = 0;
a = num % 10;
b = num / 10 % 10;
c = num / 100;
if(a*a*a+b*b*b+c*c*c == num){
cout << num << endl;
}
}while(num < 100);
system("pause");
return 0;
}
for 循环语句
功能:满足循环条件,执行循环语句
语法:
for(起始表达式;条件表达式;末尾循环体){循环语句;}
int main(){
for (int i = 0; i < 10; i++){
cout << i << endl;
}
system("pause");
return 0;
}
敲桌子
int main(){
//1.先输出1-100
for(int i = 1l i <= 100; i++){
//2.如果是7的倍数,个数有7,十位有7,打印敲桌子
if(i % 7 ==0 || i % 10 == 7 || i // 7 == 7){
cout << "敲桌子" << endl;
}
else{
cout << i << endl;
}
system("pause");
return 0;
}
}
break
功能:用于跳出条则结构或者循环结构
使用时机:
- 出现在 switch 条件语句中,作用是执行 case 条件后跳出 switch。
- 出现在循环语句中,作用是跳出循环。
- 出现在嵌套循环中,作用是跳出内层循环。
int main(){
//1.先输出1-100
for(int i = 1l i <= 100; i++){
//2.如果是7的倍数,个数有7,十位有7,打印敲桌子
if(i % 7 ==0 || i % 10 == 7 || i // 7 == 7){
break;
}
else{
cout << i << endl;
}
system("pause");
return 0;
}
}
continue
功能:在循环语句中,跳出本次循环,继续执行下次循环。
int main(){
for(int i = 0; i <= 10; i++){
if(i == 5){
continue;
}
else{
cout << i << endl;
}
}
system("pause");
return 0;
}
goto
功能:可以无条件跳转语句
语法:
goto 标记;
解释:如果标记的名称存在,执行到 goto 语句时,会跳转到标记的位置。
int main(){
cout << "1" << endl;
goto FLAG;
cout << "2" << endl;//这段代码不会执行
FLAG:
cout << "5" << endl;
system("pause");
return 0;
}
数据结构
数组
所谓数组,就是一个集合,里面存放着相同类型的数据元素。
- 在 C++中,数组中的每个元素的数据类型都是相同的。
- 在 C++中,数组是由连续的内存空间组成的。
一维数组
定义方式
数据类型 数组名称 [数组长度];
数据类型 数组名称 [数组长度] = {值1, 值2, 值3...};
数据类型 数组名[] = {值1, 值2, ...}
int main(){
int score[10];
score[0] = 100,
score[1] = 200,
int arr2[5] = {10,20,30,40,50};
cout << arr2[0] << endl;
cout << arr2[1] << endl;
cout << arr2[2] << endl;
cout << arr2[3] << endl;
cout << arr2[4] << endl;
system("pause");
return 0;
}
一维数组数组名
一维数组名称的用途
可以统计整个数组在内存中的长度,可以获取数组在内存的首地址。
数组的属性:
- 连续内存分配,数组分配连续的内存块来存储元素,这允许有效的随机访问元素。
- 固定大小:一旦声明,数组的大小就无法更改。
- 数组类型同质性:数组中的所有元素必须具有相同的数据类型。
- 手动内存管理:数组需要使用指针以及
new
和delete
运算符进行手动内存管理。
常见的数组操作:
- 遍历数组:使用循环迭代数组的所有元素(for)。
- 搜索数组:使用搜索算法查找数组中特定的元素。
- 对数组进行排序:按特定顺序(例如升序,降序)重新排列数组的元素。
- 插入元素:向数组添加新元素(如果数组支持动态调整大小)。
- 删除元素:从数组中删除元素。
数组的应用:
存储数据集合:数组是存储相关数据集合的一种简单有效的方法,例如学生成绩、员工记录、或传感器读数。
实现数据结构:数组构成了更复杂的数据结构(如链表、堆栈和队列)的基础。
算法和数值计算:数组通常用于对数据序列进行操作的算法中,例如排序、搜索和矩阵运算。
系统编程和硬件接口:数组在系统编程中用于与硬件设备交互并管理内存分配。
#include <iostream>
// #include"head.h"
using namespace std;
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++)
{
cout << i << endl;
}
return 0;
}
声明和初始化
int arr1[5] = {1,2,3,4,5};
double arr2[5] = {1.1,2.2,3.3,4.4,5.5};
string arr3[5] = {"张三","李四","王五","赵六","孙七"};
案例:五只小猪称体重
在一个数组中记录五只小猪的体重,找出来并打印出来。
int main(){
//创建五只小猪的体重
int arr[5] = {300,350,200,400,250};
//从数组中找到最大值
int max = 0; // 先认定一个最大值
for(int i = 0; i < 5; i++){
if(arr[i] > max){
max = arr[i];
}
}
cout << max << endl;
system("pause");
return 0;
}
数组元素逆置
声明五个元素的数组,将里面的元素逆置。
如:[1,2,3,4,5] => [5,4,3,2,1]
#include <iostream>
// #include"head.h"
using namespace std;
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
// Reverse the array
for (int i = 0, j = 4; i < j; i++, j--)
{
swap(arr[i], arr[j]);
}
// Print the reversed array
for (int i = 0; i < 5; i++)
{
cout << arr[i] << "";
}
cout << " " << endl;
return 0;
}
查询数组元素数量
#include <iostream>
// #include"head.h"
using namespace std;
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
int a = sizeof(arr[0]); // 确定数组中单个元素所占用的字节
int b = sizeof(arr); // 确定数组中所有元素所占用的字节
cout << b / a << endl; // 将总大小除以单个元素的字节,然后输出的就是数组中元素的数量
}
冒泡排序
#include <iostream>
// #include"head.h"
using namespace std;
int main()
{
int arr[5] = {1, 3, 4, 5, 2};
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5 - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < 5; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
二维数组
二维数组就是在一维数组之上增加了一列。
定义方式
数据类型 数组名[行][列];
数据类型 数组名[行][列] = {数据1, 数据2},{数据3, 数据4};
数据类型 数组名[行][列] = {数据1,数据2,数据3,数据4};
数据类型 数组名[][列] = {数据1,数据2,数据3,数据4};
以上四种方式 ,使用第二种更加直观。代码可读性高。
访问元素
要访问二维数组中的元素,可以使用方括号扩起来的行索引和列索隐,行索引从0开始,列索引也从0开始。
#include <iostream>
// #include"head.h"
using namespace std;
int main()
{
int arr[2][2] = {{1, 2}, {3, 4}};
cout << arr[0][0] << endl;
cout << arr[0][1] << endl;
cout << arr[1][0] << endl;
cout << arr[1][1] << endl;
return 0;
}
遍历和处理
使用嵌套for循环来迭代2d数组的元素。例如要打印arr
数组的所有元素:
#include <iostream>
// #include"head.h"
using namespace std;
int main()
{
int arr[2][2] = {{1, 2}, {3, 4}};
for (int row = 0; row < 2; row++)
{
for (int col = 0; col < 2; col++)
{
cout << arr[row][col] << endl;
}
}
return 0;
}
动态内存分配
对于可以在运行时调整大小的动态二维数组,请考虑使用std::vector<std::vector<int>>
C++标准库中的数组。
#include <vector>
int main() {
std::vector<std::vector<int>> dynamicNumbers(3, std::vector<int>(4)); // Dynamic 2D array
// Adding elements
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 4; col++) {
dynamicNumbers[row][col] = row * col;
}
}
// Accessing and printing elements
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 4; col++) {
std::cout << dynamicNumbers[row][col] << " ";
}
std::cout << std::endl;
}
return 0;
}
二维数组的优点:
- 结构化数据存储:它们提供了一种结构化的方式来组织和管理行和列中的数据。
- 高效的内存使用:它们可以高效地存储具有规则模式的数据。
- 通用型:适用于图像处理、矩阵运算、游戏开发等多种场景。
注意:
- 大小限制:静态二维数组具有在编译时声明的固定大小。
- 手动内存管理:对于动态二维数组,需要手动管理内存分配和释放以避免内存泄漏。
函数
功能:将经常使用的代码封装起来,目的是减少重复代码。
一个较大的软件会分为若干的程序块,每个模块实现特定的功能。
定义:
- 返回值类型
- 函数名称
- 参数列表
- 函数体语句
- return表达式
语法:
返回值类型 函数名称(参数列表){函数体语句 return表达式}
#include <iostream>
// #include"head.h"
using namespace std;
int add(int a, int b)
{
return a + b;
}
int main()
{
int c = add(1, 2);
cout << c << endl;
}
函数的调用
功能:使用函数的功能,函数调用时,有参数写上参数,没有参数不写参数。
语法:
函数名称() //没有参数的调用方法
函数名称(参数) //有参数的调用方法
值传递
函数参数通常按值传递,意味着实际值的副本将传递给函数。这意味着对函数内的喊出参数所做任何修改都不会影响调用者传递的原始值。
价值传递的好处:
- 原始值的保护:它可以防止对调用者传递的原始值进行意外的修改。
- 简单类型的效率:对于整数等小型数据类型,复制值通常是最高效的。
- 可预测的行为:它确保可预测的行为,因为函数无法意外更改调用者的数据。
何时使用值传递:
- 简单的数据类型:整数、浮点数、双精度数等。
- 不需要修改的函数参数:当函数不应更改从调用者传递的原始值时。
- 传递可能被复制的函数参数:对于大型或者复杂的数据和机构,复制可能比通过引用传递更加有效果。
值传递是C++函数参数中的基本概念,它确保函数对原始值的副本进行操作,从而防止对调用者数据的意外修改。理解值传递哦对于编写干净、可预测和可维护的C++代码至关重要。
函数的常见样式
- 无参无返
- 有参有返
- 无参有返
- 有参有返
#include <iostream>
// #include"head.h"
using namespace std;
//无参无返
void test01()
{
cout << "hello" << endl;
}
//有参有返
void test02(int a)
{
cout << "world" << endl;
}
//无参有返
int test03()
{
return 100;
}
//有参有返
int test04(int a)
{
return a;
}
//主函数
int main()
{
test01();
test02(10);
cout << test03() << endl;
cout << test04(100) << endl;
return 0;
}
函数的分文件编写
功能:让代码结构清晰
函数分文件编写分为四个步骤
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
//swap.h
#include <iostream>
using namespace std;
void swap(int a, int b);
//swap.cpp
#include <iostream>
using namespace std;
#include "swap.h"
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
指针
功能:指针是一个变量,可以存储一个对象的内存地址。 指针在 C 和 C++ 中广泛用于三个主要用途:
- 在堆上分配新对象,
- 将函数传递给其他函数
- 循环访问数组或其他数据结构中的元素。
c++提供列智能指针用于分配对象,提供了迭代器用于遍历数据结构,还提供了Lambda表达式用于传递函数。通过使用这些语言和库设施,而不是原始指针,可使程序更安全、易于调试。
语法:
数据类型 * 变量名;
// int * a;
#include<iostream>
using namespace std;
int main(){
int b = 10;
int * a;
a = &b;
cout << b << endl;
cout << a << endl;
cout << *a << endl;
return 0;
}
指针所占用内存永健
指针也是一种数据类型,name这种数据类型占用多少个内存空间。
int main(){
int a = 10;
int *P;
p = &a;
cout << *P << endl;
cout << sizeof(p) << endl;
cout << sizeof(int *) << endl;
cout << sizeof(char *) << endl;
cout << sizeof(float *) << endl;
cout << sizeof(double *) << endl;
return 0;
}
注意:32位操作系统,指针占用4个字节,64位操作系统占用8个字节。
空指针
空指针:指针变量指向内存编号为0的空间。
用途:初始化指针变量。
注意:空指针指向的内存是不可以访问的
int main(){
int *p = NULL;
cout << *p << endl;
}
野指针
什么是野指针?
野指针是指向无效内存区域的桌子很。无效内存区域可能已经被释放的内存、未初始化的内存或者程序无权访问的内存区域。
int main(){
int * p = (int*)0x1100;
cout << *p << endl;
return 0;
}
注意:空指针和野指针都不是我们申请的空间,因此不要访问它。
野指针的危害
野指针的危害主要有以下方面
- 程序崩溃:访问野指针通过会导致程序崩溃,因为操作系统不允许程序访问无效的内存地址。
- 数据损坏:如果通过野指针修改了内存中的数据,则会导致数据损坏。
- 安全漏洞:野指针可以被利用来进行安全攻击,例如缓冲区溢出攻击。
野指针产生的原因
- 使用为初始化的指针:在定义指针变量之后对它进行初始化,则该指针可能指向野指针。
- 指向已被释放的内存:如果一个指针指向的内存已经被释放了,则该指针就变成了野指针。
- 指向非法的内存地址:如果一个指针指向的内存地址是非法的,则该指针也是野指针。
如何避免野指针
- 初始化指针, 在定义指针变量之后,应立即对其进行初始化,使其指向合法的内存地址。
- 检查指针值:在使用指针之前,应先检查其值是否为空或者非法。
- 使用智能指针:智能指针可以自动管理内存,避免内存泄漏和野指针的产生。
智能指针
智能指针是一种用于自动管理的指针类,智能指针可以自动释放所指向的内存,避免内存泄漏和野指针产生。C++11标准库提哦给你了三种智能指针模型。
std::shared_ptr
:共享指针,支持多个指针共享同一块内存。std::unique_ptr
:唯一指针,只能由一个指针指向同一块内存。std::weak_ptr
:弱指针,不控制所指向的内存,但可以用于检测共享指针是否仍然有效。
使用智能指针
智能指针的使用非常简单,和普通指针类似。
- 创建智能指针:可以使用
std::make_shared()
、std::make_unique()
或std:: weak_ptr()
函数创建智能指针。 - 获取原始指针:可以使用
get()
函数获取智能指针所指向的原始指针。 - 重置智能指针:可以使用
reset()
函数重置智能指针,使其指向新的内存或空指针。 - 析构智能指针:智能指针的析构函数会自动释放所指向的内存。
//共享指针
std::shared_ptr<int> p = std::make_shared<int>(10);
std::shared_ptr<int> q = p;
//唯一指针
std::unique_ptr<int> r = std::make_unique<int>(20);
//弱指针
std::weak_ptr<int> w = q;
//获得原始指针
int *i = p.get();
//重置智能指针
p.reset(new int(30));
//析构智能指针
p = nullptr; // 析构 p
q = nullptr; // 析构 q
修饰指针
const修饰指针。常量指针
const修饰常量 指针常量
const即修饰指针,又修饰常量。
c++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 = 100; //报错 p3 = &b;//报错 system("pause"); return 0; }
指针和数据配合
功能:利用指针访问数组中的元素
c++int main(){ int arr[] = {1,2,3,4,5}; int * p = arr; cout << arr[0] << endl; cout << *p << endl; for(int i = 0; i < 10; i++){ cout << *p << endl; p++; } return 0; }