CPP基础内容
相关内容来自B站黑马程序员:C++教程从0到1入门编程
C++起步知识
注释
和Java一样,C++的注释分为单行注释和多行注释
单行注释://
多行注释:/* */
例如:
// 1. 这是单行注释
/*
1. 这是多行注释
2. 这是多行注释
3. 这是多行注释
*/
入口函数
在C++中,入口函数为main函数,每个程序有且仅有一个入口函数,对应Java中的public static void main
#include <iostream>
using namespace std;
int main() {
cout << "Hello World" << endl;
return 0;
}
变量
作用:给一段指定的内存空间起名,方便操作这段内存
语法:数据类型 变量 = 值;
#include <iostream>
using namespace std;
int main() {
int a = 10;
cout << "a = " << a << endl;
return 0;
}
变量命名规则:
- 字母、数字和下划线:变量名可以包含字母(A-Z, a-z)、数字(0-9)和下划线(_)。
- 以字母或下划线开头:变量名必须以字母或下划线开头,不能单独使用数字开头。
- 区分大小写:C++是区分大小写的语言,因此
variable和Variable会被视为两个不同的标识符。 - 避免使用保留关键字:避免使用C++的保留关键字作为变量名。例如,
int,class,if,for等是C++的关键字,不能用作变量名。
| asm | do | if | return | typedef |
|---|---|---|---|---|
| auto | double | inline | short | typeid |
| bool | dynamic_cast | int | signed | typename |
| break | else | long | sizeof | union |
| case | enum | mutable | static | unsigned |
| catch | explicit | namespace | static_cast | using |
| char | export | new | struct | virtual |
| class | extern | operator | switch | void |
| const | false | private | template | volatile |
| const_cast | float | protected | this | wchar_t |
| continue | for | public | throw | while |
| default | friend | register | true | |
| delete | goto | reinterpret_cast | try |
常量
作用:用于记录程序中不可更改的数据
定义方式:
#define宏常量:#define 常量名 常量值- 通常在文件上方定义,表示一个常量,常量名一般都是以全部大写命名const修饰的变量:const 数据类型 常量名 = 常量值- 通常在变量定义前加关键字const,修饰该变量为常量,不可修改 - 命名规则与变量一样
#include <iostream>
#define PI 3.141
using namespace std;
int main() {
// PI = 10 报错!
cout << "PI = " << PI << endl;
const int minutesInOneHour = 60;
// minutesInOneHour = 100 报错!
cout << "一小时有:" << minutesInOneHour << "分钟" << endl;
return 0;
}
数据类型
C++规定在创建一个变量或常量时,必须要指定相应的数据类型,否则无法给变量分配内存
整型
整型也称整数类型,C++有以下几种整型类型:
| 数据类型 | 占用空间 | 取值范围 |
|---|---|---|
| short(短整型) | 2字节 | (-2^15 ~ 2^15-1) |
| int(整型) | 4字节 | (-2^31 ~ 2^31-1) |
| long(长整形) | Windows为4字节,Linux为4字节(32位),8字节(64位) | (-2^31 ~ 2^31-1) |
| long long(长长整形) | 8字节 | (-2^63 ~ 2^63-1) |
sizeof关键字
作用:利用sizeof关键字可以统计数据类型所占内存大小
语法:sizeof(数据类型/变量)
#include <iostream>
using namespace std;
int main() {
char h = 'h';
cout << sizeof(int) << endl;// 4
cout << sizeof(h) << endl;// 1
return 0;
}
浮点型
作用:表示小数
浮点型分为两种:
- 单精度float
- 双精度double
| 数据类型 | 占用空间 | 有效数字范围 |
|---|---|---|
| float | 4字节 | 7位有效数字 |
| double | 8字节 | 15~16位有效数字 |
#include <iostream>
using namespace std;
int main() {
float a = 3.1415f;
double b = 1.01;
float c = 1.0000001f;
cout << a << endl;// 3.1415
cout << b << endl;// 1.01
cout << c << endl;// 1默认情况下,输出一个小数只会显示6位有效数字
// 科学计数法
float f1 = 3e2;// 表示 3 * 10 ^ 2,即300
float f2 = 3e-2;// 表示 3 * 0.1 ^ 2,即0.03
return 0;
}
精度丢失问题:
- 十进制 0.1 的二进制是无限循环小数:
0.1(十进制) =0.00011001100110011...(二进制),就像十进制的 1/3 = 0.3333…。
案例:
float count = 0.1 + 0.2;// 如果将小数后全部显示出来,结果是:0.30000000000000004
字符型
作用:字符型变量用于显示单个字符
语法:char ch = 'a';
注意:在显示字符变量时,用单引号将字符括起来,而不是双引号
注意:单引号内只能有一个字符,不能是字符串
- C和C++中字符型变量只占用1个字节
- 字符型变量并不是把字符本身放到内存中存储,而是将字符对应的ASCII编码放入到存储单元
#include <iostream>
using namespace std;
int main() {
char c1 = 'h';
cout << c1 << endl;// 输出:h
cout << sizeof(c1) << endl; // 输出:1
//char c2 = "h";报错,不能使用双引号
//char c3 = 'hello'报错,单引号内不能是为字符串
char c4 = 'a';
cout << (int)c4 << endl;// 97
return 0;
}
ASCII表:
| ASCII值 | 控制字符 | ASCII值 | 字符 | ASCII值 | 字符 | ASCII值 | 字符 |
|---|---|---|---|---|---|---|---|
| 0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
| 1 | SOH | 33 | ! | 65 | A | 97 | a |
| 2 | STX | 34 | “ | 66 | B | 98 | b |
| 3 | ETX | 35 | # | 67 | C | 99 | c |
| 4 | EOT | 36 | $ | 68 | D | 100 | d |
| 5 | ENQ | 37 | % | 69 | E | 101 | e |
| 6 | ACK | 38 | & | 70 | F | 102 | f |
| 7 | BEL | 39 | , | 71 | G | 103 | g |
| 8 | BS | 40 | ( | 72 | H | 104 | h |
| 9 | HT | 41 | ) | 73 | I | 105 | i |
| 10 | LF | 42 | * | 74 | J | 106 | j |
| 11 | VT | 43 | + | 75 | K | 107 | k |
| 12 | FF | 44 | , | 76 | L | 108 | l |
| 13 | CR | 45 | - | 77 | M | 109 | m |
| 14 | SO | 46 | . | 78 | N | 110 | n |
| 15 | SI | 47 | / | 79 | O | 111 | o |
| 16 | DLE | 48 | 0 | 80 | P | 112 | p |
| 17 | DCI | 49 | 1 | 81 | Q | 113 | q |
| 18 | DC2 | 50 | 2 | 82 | R | 114 | r |
| 19 | DC3 | 51 | 3 | 83 | S | 115 | s |
| 20 | DC4 | 52 | 4 | 84 | T | 116 | t |
| 21 | NAK | 53 | 5 | 85 | U | 117 | u |
| 22 | SYN | 54 | 6 | 86 | V | 118 | v |
| 23 | TB | 55 | 7 | 87 | W | 119 | w |
| 24 | CAN | 56 | 8 | 88 | X | 120 | x |
| 25 | EM | 57 | 9 | 89 | Y | 121 | y |
| 26 | SUB | 58 | : | 90 | Z | 122 | z |
| 27 | ESC | 59 | ; | 91 | [ | 123 | { |
| 28 | FS | 60 | < | 92 | / | 124 | |
| 29 | GS | 61 | = | 93 | ] | 125 | } |
| 30 | RS | 62 | > | 94 | ^ | 126 | ` |
| 31 | US | 63 | ? | 95 | _ | 127 | DEL |
ASCII 码大致由以下两部分组成:
- ASCII 非打印控制字符: ASCII 表上的数字 0-31 分配给了控制字符,用于控制像打印机等一些外围设备。
- ASCII 打印字符:数字 32-126 分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。
转义字符
作用:用于表示一些不能显示出来的ASCII字符
常用转移字符有:\n \\ \t
| 转义字符 | 含义 | ASCII码值(十进制) |
|---|---|---|
| \a | 警报 | 007 |
| \b | 退格(BS) ,将当前位置移到前一列 | 008 |
| \f | 换页(FF),将当前位置移到下页开头 | 012 |
| \n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
| \r | 回车(CR) ,将当前位置移到本行开头 | 013 |
| \t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
| \v | 垂直制表(VT) | 011 |
| \ | 代表一个反斜线字符”" | 092 |
| ' | 代表一个单引号(撇号)字符 | 039 |
| " | 代表一个双引号字符 | 034 |
| ? | 代表一个问号 | 063 |
| \0 | 数字0 | 000 |
| \ddd | 8进制转义字符,d范围0~7 | 3位8进制 |
| \xhh | 16进制转义字符,h范围0~~9,a~~f,A~F | 3位16进制 |
#include <iostream>
using namespace std;
int main() {
cout << "Hello World" << endl;
cout << "\t Hello World" << endl;
cout << "\n Hello World" << endl;
cout << "\\ Hello World" << endl;
return 0;
}
字符串型
作用:用于表示一串字符
两种风格:
- C风格字符串:
char 变量名[] = "字符串值"; - C++风格字符串:
string 变量名 = "字符串值";
#include <iostream>
using namespace std;
int main() {
string s1 = "Hello World";
char s2[] = "Hello World";
cout << s1 << endl;
cout << s2 << endl;
cout << sizeof(s1) << endl;// 40
cout << sizeof(s2) << endl;// 12
return 0;
}
注意:字符串一定要用双引号括起来
注意:虽然string和char数组都可以表示字符串,但是所占的内存大小并不一样
| 类型 | string类对象(s1) |
char[]数组(s2) |
|---|---|---|
| 内存布局 | 类实例,含指针、容量、大小等元数据 | 连续字符序列,结尾可能含\0 |
| sizeof结果 | 固定元数据大小(与字符串无关) | 整个数组的字节数(包含所有元素) |
布尔类型
作用:布尔类型表示值的真或假
布尔类型只有两种值:
true:表示值为真,本质是1false:表示值为假,本质是0
#include <iostream>
using namespace std;
int main() {
bool flag_1 = true;
bool flag_2 = false;
cout << flag_1 << endl;// 1
cout << flag_2 << endl;// 0
cout << sizeof(flag_1) << endl; // 1
return 0;
}
数据输入
作用:通过键盘输入数据
语法:cin >> 变量
#include <iostream>
using namespace std;
int main() {
int number = 0;
cout << "请输入数据:" << endl;
cin >> number;
cout << "您输入的是数字:" << number << endl;
return 0;
}
数据类型之间的转换
#include <iostream>
#include <string>// 引入string库,用于处理string数据类型
#include <typeinfo> // 引入typeinfo,用于判断数据类型
using namespace std;
int main() {
// 字符串转整数
string s1 = "123";
int n1 = stoi(s1);
n1++;
cout << n1 << endl;// 124
// 整数转字符串
int n2 = 11;
string s2 = to_string(n2) + to_string(n2);
cout << s2 << endl;// 1111
// 浮点数转整数
float f1 = 3.11f;
int n3 = (int)f1;
cout << n3 << endl;// 3向下取整,也称为小数部分截断
// 整数转浮点数
int n4 = 10;
float f2 = (float)n4;
f2 += 0.1;
cout << f2 << endl;// 10.1
// 浮点数转字符串
float f3 = 0.5f;
string s3 = to_string(f3) + to_string(f3);
cout << s3 << endl;// 0.5000000.500000
// 字符串转浮点数
string s4 = "0.345";
float f4 = stof(s4);
f4 += 0.1;
cout << f4 << endl;// 0.445
return 0;
}
运算符
作用:用于执行代码的运算
| 运算符类型 | 作用 |
|---|---|
| 算术运算符 | 用于处理四则运算 |
| 赋值运算符 | 用于将表达式的值赋给变量 |
| 比较运算符 | 用于表达式的比较,并返回一个真值或假值 |
| 逻辑运算符 | 用于根据表达式的值返回真值或假值 |
算数运算符
| 运算符 | 术语 | 示例 | 结果 |
|---|---|---|---|
| + | 正号 | +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; |
注意:整数与整数相除,结果一定是整数,如果除不尽则向下取整,如
10 / 3 = 3注意:整数与浮点数进行加减乘除,结果一定是浮点数
注意:小数不能做取模运算
赋值运算符
作用:用于将表达式的值赋给变量
| 运算符 | 术语 | 示例 | 结果 |
|---|---|---|---|
| = | 赋值 | 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; |
比较运算符
作用:用于表达式的比较,并返回一个真值或假值
| 运算符 | 术语 | 示例 | 结果 |
|---|---|---|---|
| == | 相等于 | 4 == 3 | 0 |
| != | 不等于 | 4 != 3 | 1 |
| < | 小于 | 4 < 3 | 0 |
| > | 大于 | 4 > 3 | 1 |
| <= | 小于等于 | 4 <= 3 | 0 |
| >= | 大于等于 | 4 >= 1 | 1 |
#include <iostream>
using namespace std;
int main() {
int num_01 = 10;
int num_02 = 20;
cout << (num_01 < num_02) << endl;// 1
cout << (num_01 > num_02) << endl;// 0
cout << (num_01 == num_02) << endl;// 0
cout << (num_01 <= num_02) << endl;// 1
cout << (num_01 >= num_02) << endl;// 0
return 0;
}
逻辑运算符
作用:用于根据表达式的值返回真值或假值
| 运算符 | 术语 | 示例 | 结果 |
|---|---|---|---|
| ! | 非 | !a | 如果a为假,则!a为真; 如果a为真,则!a为假。 |
| && | 与 | a && b | 如果a和b都为真,则结果为真,否则为假。 |
| 或 |
#include <iostream>
using namespace std;
int main() {
int num_01 = 10;
int num_02 = 20;
int num_03 = 30;
cout << !(num_01 < num_02) << endl;// 0
cout << !(num_01 > num_02) << endl; // 1
cout << (num_01 < num_02 && num_02 < num_03) << endl;// 1
cout << (num_01 < num_02 && num_02 > num_03) << endl;// 0
cout << (num_01 > num_02 || num_02 > num_03) << endl;// 0
cout << (num_01 < num_02 || num_02 < num_03) << endl;// 1
return 0;
}
程序流程结构
C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构
- 顺序结构:程序按顺序执行,不发生跳转
- 选择结构:依据条件是否满足,有选择的执行相应功能
- 循环结构:依据条件是否满足,循环多次执行某段代码
选择结构
if语句
作用:执行满足条件的语句
if语句的三种形式:
- 单行格式
if语句 - 多行格式
if语句 - 多条件的
if语句
- 单行格式
if语句:if (条件) {条件满足执行的语句}

#include <iostream>
using namespace std;
int main() {
int num = 0;
cout << "请输入一个数字:" << endl;
cin >> num;
if (num < 10) {
cout << "你输入的数字比10小" << endl;
}
if (num >= 10 && num <= 20) {
cout << "你输入的数字在10 ~ 20之间" << endl;
}
if (num > 20) {
cout << "你输入的数字比20大" << endl;
}
return 0;
}
- 多行格式
if语句:if (条件) {条件满足执行的语句} else { 条件不满足执行的语句 };

#include <iostream>
using namespace std;
int main() {
int num = 0;
cout << "请输入一个数字:" << endl;
cin >> num;
if (num < 10) {
cout << "你输入的数字比10小" << endl;
}
else {
cout << "你输入的数字比10大" << endl;
}
return 0;
}
- 多条件
if语句:if (条件1) {条件1满足执行的语句} else if (条件2) {条件2满足执行的语句} ... else {都不满足执行的语句}
#include <iostream>
using namespace std;
int main() {
int num = 0;
cout << "请输入一个数字:" << endl;
cin >> num;
/*
if (num > 0) {
cout << "你输入的数字比0大" << endl;// 只要数字比0大,就只会执行部这一条,后面的条件都会跳过
}
else if (num > 10){
cout << "你输入的数字比10大" << endl;
}
else if (num > 20) {
cout << "你输入的数字比20大" << endl;
}
else {
cout << "你输入的数字很大" << endl;
}
*/
if (num < 10) {
cout << "你输入的数字比10小" << endl;
}
else if (num < 20) {
cout << "你输入的数字比10大比20小" << endl;
}
else if (num < 30) {
cout << "你输入的数字比20大比30小" << endl;
}
else {
cout << "你输入的数字大于等于30" << endl;
}
return 0;
}
if语句嵌套
#include <iostream>
using namespace std;
int main() {
int age;
double height;
cout << "请输入年龄: ";
cin >> age;
cout << "请输入身高(cm): ";
cin >> height;
// 外层判断年龄
if (age >= 18) {
// 内层判断身高
if (height >= 160.0) {
cout << "符合条件,允许乘坐过山车!" << endl;
} else {
cout << "年龄达标,但身高不足160cm,不可乘坐" << endl;
}
} else {
cout << "年龄未满18岁,禁止乘坐" << endl;
}
return 0;
}
三目运算符
作用:通过三目运算符实现简单的判断
语法:表达式1 ? 表达式2 : 表达式2;
解释:
如果表达式1的值为真,执行表达式2,并返回表达式2的结果
如果表达式2的值为假,执行表达式3,并返回表达式3的结果
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
int c = 0;
a < b ? cout << "a小于b" << endl : cout << "a大于b" << endl;
c = a > b ? a : b;// 三目运算符能返回结果,所以可以用于赋值
cout << "c的值为:" << c << endl;
return 0;
}
switch语句
作用:执行多条件分支语句
语法:
switch(表达式)
{
case 结果1: 执行语句1;break;
case 结果2: 执行语句2;break;
...
default:执行结果;break;
}
示例:
#include <iostream>
using namespace std;
int main() {
int a = 0;
cout << "请输入一个数:" << endl;
cin >> a;
switch (a) {
case 1:
cout << "值为1" << endl;
break;
case 2:
cout << "值为2" << endl;
break;
default:
cout << "值不为1也不为2" << endl;
break;
}
return 0;
}
如果不写break,switch会继续向下寻找符合条件的case并执行语句,这也称为case穿透:
#include <iostream>
using namespace std;
int main() {
int a = 0;
cout << "请输入一个数:" << endl;
cin >> a;
switch (a) {
case 0:;
case 1:;
case 2:;
case 3:;
case 4:
cout << "值在0到4之间";
break;
case 5:;
case 6:;
case 7:;
case 8:;
case 9:
cout << "值在5到9之间";
break;
default:
cout << "值大于等于10";
break;
}
return 0;
}
循环结构
while循环语句
作用:满足循环条件,执行循环语句
语法:while(循环条件) {循环语句}
解释:只要循环条件的结果为真,就执行循环语句

示例:
#include <iostream>
using namespace std;
int main() {
int i = 0;
while (i < 10) {
cout << "我今年" << i << "岁了" << endl;
i++;
}
i = 0;
while (i < 100) {
if (i == 18) {
cout << "我成年啦" << endl;
break;// 跳出循环
}
else {
cout << "我今年" << i << "岁了" << endl;
i++;
}
}
i = 0;
while (i < 100) {
if (i > 30 && i < 50) {
i++;
cout << "我要开始认真工作了" << endl;
continue;// 跳过一次循环
}
cout << "我今年" << i << "岁了" << endl;
i++;
}
return 0;
}
注意:一定要注意避免死循环,如
while (1),如果循环体中没有中断循环的代码,程序会一直运行直到内存资源消耗殆尽
do…while循环语句
作用:满足循环条件,执行循环语句
语法:do {循环语句} while {循环条件};
解释:在循环前先执行一次操作再判断循环条件
#include <iostream>
using namespace std;
int main() {
int password;
do {
cout << "请输入密码(必须>0):";
cin >> password;
} while (password <= 0); // 强制用户至少输入一次
return 0;
}
练习案例——水仙花数
水仙花数是指一个三位数,它的每个位上的数字的3次幂之和等于它本身
例如:1^3 + 5^3 + 3^3 = 153
利用do…while求出三位数中所有的水仙花数
#include <iostream>
using namespace std;
int main() {
int num = 100;
do {
int ge = num % 100 % 10;
int shi = num / 10 % 10;
int bai = num / 100;
if (pow(ge, 3) + pow(shi, 3) + pow(bai, 3) == num) {
cout << num << "是水仙花数" << endl;
num++;
continue;
}
num++;
} while (num < 1000);
return 0;
}
for循环语句
作用:满足循环条件,执行循环语句
语法:for(起始表达式;条件表达式;末尾循环体) {循环语句;}
int main() {
for (int i = 0;i < 10; i++) {
cout << i << endl;
}
return 0;
}
嵌套循环
作用:在循环体中再嵌套一层循环
示例:
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i < 10; i++)
{
for (int j = 1; j <= i; j++) {
cout << j << "x" << i << "=" << i * j << " ";
}
/*
// 下面这种写法更繁琐,消耗资源更多,但是好理解
for (int j = 1; j < 10; j++) {
if (j > i) {continue;}
cout << j << "x" << i << "=" << i * j << " ";
}
*/
cout << " " << endl;
}
return 0;
}
goto语句
作用:可以无条件跳转到语句
语法:goto 标记;
解释:如果标记的名称存在
#include <iostream>
using namespace std;
int main() {
cout << "1" << endl;
goto Flag;
cout << "2" << endl;
cout << "3" << endl;
cout << "4" << endl;
Flag:
cout << "5" << endl;
cout << "6" << endl;
cout << "7" << endl;
cout << "8" << endl;
return 0;
}
注意:由于goto代码会打乱代码的执行顺序,导致代码可读性变差,所以尽量避免使用
数组
数组就是一个集合,里面存放了相同类型的数据元素
特点1:数组中的每个数据元素都是相同的数据类型
特点2:数组是由连续的内存位置组成的
静态初始化
方式1(静态显式初始化):
语法:数据类型 数组名[数组长度] = {值1,值2,...,不能超过数组最大长度} ;
int arr_01[5] = {1,2,3,4,5};// 完全显式初始化
int arr_02[5] = {1,2,3};// 部分显式初始化,没有声明具体值的部分记为0
int arr_03[5] = {1,2,3,4,5,6};// 报错!超过数组最大长度
int arr_04[5]{1,2,3}// C++11统一初始化,没有声明具体值的部分记为0
方式2(零初始化):
当前不考虑局部作用域
语法:数据类型 数组名[数组长度];
int arr_01[5];
int arr[5]{}; // C++11统一初始化(推荐)
arr_01[0] = 1;
arr_01[1] = 2;
arr_01[2] = 3;
arr_01[3] = 4;
arr_01[4] = 5;
arr_01[5] = 6;// 报错!超过数组最大长度
方式3(推导式静态显式初始化):
语法:数据类型 数组名[] = {1,2,3,4,...,没有长度限制}
int arr_01[] = {1,2,3,4,5};
int arr_02[] = {1,2};
int arr_03[] = {1,2,3,4,5,6,7,8,9,10,11,...}
获取数组长度
C++17以前没有Python中len(数组名)或Java中数组名.length()来快速获取数组长度,需要进行单独的计算
计算公式:sizeof(数组名) / sizeof(数组数据类型)
解释:sizeof(数组名)表示数组中所有元素在内存中所占的大小,sizeof(数组数据类型)表示数组的数据类型所占内存空间大小
例如:
int arr[] = {1,2,3,4,5,6,7,8,9};
int len_01 = sizeof(arr) / sizeof(int);
// 或者
int len_02 = sizeof(arr) / sizeof(arr[0]);// 用于当不能确定数组的具体数据类型时(推荐)
C++17之后可以使用size()快速获取数组长度:
#include <iostream>
using namespace std;
int main() {
int arr[]{ 5,2,3,6,9,7,8,1,4 };
cout << size(arr) << endl;
return 0;
}
冒泡排序
作用:最常用的排序算法,对数组内元素进行排序
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个
- 对每一个相邻元素做同样的工作,执行完毕后,找到第一个最大值
- 重复以上的步骤,每次比较次数-1,直到不需要比较
#include <iostream>
using namespace std;
void coutArr(int arr[],int length) {
for (int i = 0; i < length; i++)
{
cout << arr[i];
}
}
int main() {
int arr[]{ 5,2,3,6,9,7,8,1,4 };
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length - 1; i++)
{
for (int j = 0; j < length - 1 - i; j++) {// 这里-1-i的原因是假设第一轮比完后最后一位一定是最大的,可以跳过检测
if (arr[j] > arr[j + 1]) {// 改变符号就等于改变排序
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
coutArr(arr, length);
return 0;
}
二维数组
二维数组定义的四种方式:
数据类型 数组名[行数][列数];数据类型 数组名[行数][列数] = {{值1,值2}, {值3,值4}};数据类型 数组名[行数][列数] = {数据1, 数据2, 数据3, 数据4};数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};
推荐使用第二种方式,更加直观,提升代码可读性
示例:
#include <iostream>
using namespace std;
int main() {
int arr[2][3] = {
{1,2,3},
{4,5,6}
};
int row = sizeof(arr) / sizeof(arr[0]);
int col = sizeof(arr[0]) / sizeof(arr[0][0]);
cout << "col列:" << col << endl;
cout << "row行:" << row << endl;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++) {
cout << "第" << i + 1 << "行" << "第" << j + 1<< "列的数字是" << arr[i][j] << endl;
}
}
return 0;
}
案例
将下表内容输出的控制台中:
| 语文 | 数学 | 英语 | |
|---|---|---|---|
| 张三 | 100 | 100 | 100 |
| 李四 | 90 | 50 | 100 |
| 王五 | 60 | 70 | 80 |
答案:
#include <iostream>
using namespace std;
int main() {
// 输出成绩
int score[3][3] = {
{100,100,100},
{90,50,100},
{60,70,80}
};
string name[] = {"张三","李四","王五"};
int row = sizeof(score) / sizeof(score[0]);
int col = sizeof(score[0]) / sizeof(score[0][0]);
cout << " 语文 数学 英语" << endl;
for (int i = 0; i < row; i++)
{
cout << name[i] << " ";
for (int j = 0; j < col; j++) {
cout << score[i][j] << " ";
}
cout << endl;
}
return 0;
}
函数
作用:将一段经常使用的代码封装起来,减少重复代码
函数的定义
- 返回值类型
- 函数名
- 参数列表
- 函数体语句
- return 表达式
语法:
返回值类型 函数名 (参数列表)
{
函数体语句;
return 表达式;
}
基本使用案例:
#include <iostream>
using namespace std;
void saySomeThing(string strs) {
cout << strs << endl;
};
int sum(int num1, int num2) {
return num1 + num2;
}
int main() {
saySomeThing("Hello World");
saySomeThing("你好!!");
//saySomeThing(12345); 报错,实参数据类型与形参不匹配
int count = sum(1, 2);// 有返回值可以用变量接收
cout << sum(5, 6) << endl;// 也可以直接打印
return 0;
}
注意:函数必须在main之前声明,否则运行时会检测不到函数而引起报错
int main() {
func();// 报错
}
void func() {...};// 后声明
但是以下写法是可以的:
void func();// 先声明
int main() {
func();// 正确运行
}
void func() {
cout << "Hello World" << endl;// 再实现
}
函数的调用
以上代码已经包含了函数的调用,简单来说,调用一个函数只需要函数名(函数参数)即可
如:sum(1,2)、sayHello()、checkMission()
函数的值传递
函数定义时,括号里的参数表示形参,函数调用时,括号里的参数表示实参
值的传递流程就是:调用函数 => 修改/添加实参 => 对应位置上的形参被替换为实参 => 执行函数体内容
所以,只要实参和形参的类型相同,形参名是什么都无所谓,只要每个形参之间不重名即可
函数的常见形式
常见的函数样式有4种:
- 无参无返
- 有参无返
- 无参有返
- 有参有反
#include <iostream>
using namespace std;
// 无参无返
void test_01() { cout << "无参无返" << endl; }
// 有参无返
void test_02(int nums[]) {
for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++)
{
cout << nums[i] << " ";
}
}
// 无参有返
bool test_03() { return true; }
// 有参有返
string test_04(string s1,string s2,string s3) {
return s1 + s2 + s3;
}
int main() {
cout << "无参无返调用结果:";
test_01();
cout << "有参无返调用结果:";
int arr[]{ 1,2,3,4,5 };
test_02(arr);
cout << endl;
cout << "无参有返调用结果:";
bool b1 = test_03();
cout << b1 << endl;
cout << "有参有返调用结果:";
string s1 = test_04("你好", "世界", "Hello World");
cout << s1 << endl;
return 0;
}
函数的声明
作用:告诉编译器函数名称以及如何调用函数。函数的实际主体可以单独定义
- 函数的声明可以多次,但是函数的定义只能一次
// 声明可以多次,定义只能一次
int max(int a,int b);
int max(int a,int b);
// 定义
int max(int a,int b)
{
return a > b ? a : b;
}
int main() {
cout << max(1,2) << endl;
}
函数的重载
虽然正常情况下函数定义只能定义一次,但实际上只要形参数量不重复就可以多次定义,当实参数对应到了函数的形参数就调用哪个函数
int max(int a,int b) {// 这个函数暂时可以叫做函数一
return a > b ? a : b;
}
int max(int a,int b,int c) {// 这个函数暂时叫做函数二
return max(max(a,b),c);
}
int main() {
max(1,2);// 这里调用的是函数一
max(1,2,3);// 这里调用的是函数二
}
函数的分文件编写
作用:让代码结构更加清晰
函数分文件编写一般有4个步骤
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
示例:
// swap.h文件
#include<iostream>
using namespace std;
// 实现两个数字交换的函数声明
void swap(int a, int b);
// swap.cpp文件
#include "swap.h"
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
// main函数文件
#include "swap.h"
int main() {
int a = 100;
int b = 200;
swap(a, b);
system("pause");
return 0;
}
指针
指针的作用:可以通过指针间接访问内存
- 内存编号是从0开始记录,一般用十六进制数字表示
- 可以利用指针变量保存地址
指针变量的定义和使用
指针变量定义语法:数据类型 * 变量名
#include <iostream>
using namespace std;
int main() {
// 1. 定义指针
int a = 10;
// 指针定义的语法:数据类型 * 指针变量名
int* p;
// 让指针记录变量a的地址
p = &a;// & 表示取址符号
cout << "a的内存地址为:" << &a << endl;// 控制台输出:000000C7B31CF894
cout << "指针p的值为:" << p << endl;// 控制台输出:000000C7B31CF894
// // *指针名 表示解引用
cout << "指针p指向内存地址的具体值为:" << *p << endl; // 控制台输出:10
// 通过 *p 修改a的值
*p = 255;
cout << "*p修改a的值后,a当前的值为:" << a << endl;// 控制台输出:255
return 0;
}
&表示取值符号*指针名 = 值表示对指针所指的变量的值进行修改
int a = 10;// 将整数值10存入变量a的内存空间,此时a对应的内存地址中存储的值为10
int* p = &a;// &a获取变量a的内存地址,并将其赋值给指针变量p。此时p保存的是a的内存地址
*p = 255;// 通过解引用操作符*访问p指向的内存地址(即a的地址),修改该地址存储的值为20。此时a的值变为20。
指针所占内存空间
#include <iostream>
using namespace std;
int main() {
int a = 10;
int* p = &a;
cout << sizeof(p) << endl;// 8
cout << sizeof(char*) << endl;// 8
cout << sizeof(float*) << endl;// 8
cout << sizeof(double*) << endl;// 8
return 0;
}
| 系统架构 | 地址总线宽度 | 指针大小 | 寻址空间 |
|---|---|---|---|
| 32位 | 32位 | 4字节 | 4GB(2^32) |
| 64位 | 64位 | 8字节 | 16EB(2^64) |
- 64位系统:指针统一占用 8字节(无论指向何种数据类型)。
- 32位系统:指针统一占用 4字节。
- 原理:指针存储的是内存地址,其大小由 系统寻址能力 决定(64位系统地址总线为64位,需8字节存储)
空指针和野指针
空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
示例:
#include <iostream>
using namespace std;
int main() {
int* p= NULL;
cout << p << endl;// 报错
cout << *p << endl;// 报错
return 0;
}
野指针:指针变量指向非法的内存空间
示例:
#include <iostream>
using namespace std;
int main() {
int* p = (int *)0x1100;
cout << *p << endl;// 报错
return 0;
}
const修饰指针
const修饰指针有三种情况:
- const修饰指针
- 称为:常量指针
- 语法:
const int * p = a;- 特点:指针的指向可以修改,但是指针指向的值不可以该。如p = &b是可以的,但是*p = 20是错误的 - const修饰常量
- 称为:指针常量
- 语法:
int * const p = a;- 特点:与常量指针正好相反。p = &b是错误的,*p = 20是可以的 - const即修饰指针,又修饰常量
- 无特定名称
- 语法:
const int * const p = a;- 特点:指针的指向、指针指向的值都不可以修改。*p = 20、p = &b都是错误的
记忆技巧:
- const在
*之前,表示对*进行了限制,当操作*时就是错误的,如*p = 20 - const在
*之后,表示对指针变量进行了限制,当操作指针变量时就是错误的,如p = &b
| 类型 | 术语 | 特性总结 |
|---|---|---|
const int *p |
指向常量的指针 | 值不可改,地址可改 |
int *const p |
指针常量 | 地址不可改,值可改 |
const int *const p |
指向常量的指针常量 | 值和地址均不可改 |
示例:
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
const int* p1 = &a;
*p1 = 20;// 编译器直接报错,提示表达式必须是可以修改的左值
p1 = &b;
int* const p2 = &a;
*p2 = 20;
p2 = &b;// 编译器直接报错,提示表达式必须是可以修改的左值
const int* const p3 = &a;
*p3 = 20;// 编译器直接报错,提示表达式必须是可以修改的左值
p3 = &b;// 编译器直接报错,提示表达式必须是可以修改的左值
return 0;
}
指针和数组
作用:利用指针访问数组中元素
示例:
#include <iostream>
using namespace std;
int main() {
int arr[]{ 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;// arr存储的就是地址,不用取址符号
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
cout << "数组中索引为" << i << "的值是:" << *p << endl;// 打印的是具体的值
cout << "数组中索引为" << i << "的值的地址是:" << p << endl;
p++;// p++等价于 p = p + sizeof(int)
}
return 0;
}
关于p++的详细说明:
| 机制 | 说明 |
|---|---|
| 类型依赖步长 | 指针加减运算根据指向类型自动调整步长(int→4字节,double→8字节) |
| 编译器隐式转换 | p+1实际执行 p + 1*sizeof(int) |
指针和函数
作用:利用指针作函数参数,可以修改实参的值
示例:
#include <iostream>
using namespace std;
// 值传递函数
void swap_01(int a, int b) {
int tmp = a;
a = b;
b = tmp;
}
// 地址传递函数
void swap_02(int* p1, int* p2) {
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
// 无效的指针交换(错误原因:交换指针副本)
void swap_03(int* p1, int* p2) {
int* tmp = p1;
p1 = p2;
p2 = tmp;
}
int main() {
int a = 10;
int b = 20;
// 调用值传递函数,a和b的值并不会发生改变
swap_01(a, b);
cout << "a的值为:" << a << endl;// 10
cout << "b的值为:" << b << endl;// 20
// 调用地址传递函数,a和b的值是会发生改变的
swap_02(&a, &b);
cout << "a的值为:" << a << endl;// 20
cout << "b的值为:" << b << endl;// 10
swap_03(&a, &b);
cout << "a的值为:" << a << endl;// 20
cout << "b的值为:" << b << endl;// 10
return 0;
}
指针、函数、数组
案例:利用冒泡排序,编写一个函数对数组进行从小到大的排序
示例:
#include <iostream>
using namespace std;
void coutArr(int arr[],int length) {
for (int i = 0; i < length; i++)
{
cout << arr[i];
}
}
void BubbleSort(int arr[], int length) {
for (int i = 0; i < length - 1; i++)
{
for (int j = 0; j < length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main() {
int arr[]{ 5,2,3,6,9,7,8,1,4 };
int length = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, length);
coutArr(arr, length);
return 0;
}
结构体
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
结构体定义和使用
语法:struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
struct 结构体名 变量名struct 结构体名 变量名 = {成员值1, 成员值2...}- 定义结构体时顺便创建变量
示例:
#include <iostream>
using namespace std;
// 1. 创建学生数据类型:学生包括(姓名、年龄、分数)
struct Student
{
// 成员列表
string name;
int age;
int score;
string gender;
};
/*
// 创建具体的学生(方式3)不推荐,了解即可
struct Student
{
// 成员列表
string name;
int age;
int score;
string gender;
} s3;
*/
int main() {
// 创建具体的学生(方式1)
struct Student s1 = { "zhangsan",5,200,"男" };// 按照顺序赋值
cout << "学生叫做:" << s1.name << ";学生年龄为:" << s1.age << ";学生的成绩为:" << s1.score << ";学生的性别是:" << s1.gender << endl;
// 创建具体的学生(方式2)
struct Student s2;
s2.name = "李四";
s2.age = 8;
s2.gender = "男";
s2.score = 180;
cout << "学生叫做:" << s2.name << ";学生年龄为:" << s2.age << ";学生的成绩为:" << s2.score << ";学生的性别是:" << s2.gender << endl;
return 0;
}
注意1:当定义结构体时,struct关键字是不可以被省略的
注意2:创建结构体变量时,如
struct Student s1可以省略struct=>Student s1访问结构体变量的成员时,可以用操作符
"."访问成员
结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法:struct 结构体名 数组名[元素个数] = {{},{},...{}}
示例:
#include <iostream>
#include <string>
using namespace std;
// 1. 创建学生数据类型:学生包括(姓名、年龄、分数)
struct Student
{
// 成员列表
string name;
int age;
int score;
string gender;
};
int main() {
Student stuArry[] = {
{"张三",18,172,"男"},
{"李四",18,178,"女"},
{"王五",17,180,"女"},
{"赵六",17,180,"男"},
};
cout << sizeof(stuArry) / sizeof(stuArry[0]) << endl;// 4
cout << size(stuArry) << endl;// 4
for (int i = 0; i < size(stuArry); i++)
{
stuArry[i].name = stuArry[i].name + to_string(i);
cout << "姓名:" << stuArry[i].name << " ";
cout << "年龄:" << stuArry[i].age << " ";
cout << "分数:" << stuArry[i].score << " ";
cout << "性别:" << stuArry[i].gender << " " << endl;
}
return 0;
}
结构体指针
作用:通过指针访问结构体中的成员
- 利用操作符
->可以通过结构体指针访问结构体数学
示例:
#include <iostream>
using namespace std;
struct Subject {
string name;
string level;
int point;
bool isOpened;
};
int main() {
Subject s1 = { "英语","高",5,true };
// 通过指针指向结构体变量
Subject* p = &s1;
// 通过指针访问结构体变量中的数据
cout << p->name << endl;
return 0;
}
结构体嵌套结构体
当结构体内存在另一个结构体,就称之为结构体嵌套结构体
示例:
#include <iostream>
using namespace std;
struct Teacher
{
string name;
int age;
string gender;
};
struct Student
{
string name;
int age;
// 在结构体内直接声明
struct Subject {
string name;
string level;
int point;
bool isOpened;
} subject;
// 先在结构体外声明,最后在结构体中使用
struct Teacher teacher;
};
int main() {
Student s1;
s1.age = 18;
s1.name = "张三";
s1.subject.level = "中等";
s1.subject.name = "体育";
s1.subject.point = 3;
s1.subject.isOpened = false;
s1.teacher = { "老王",42,"男" };
cout << s1.name << "今年" << s1.age << "岁了,"
<< "他/她的班主任是" << s1.teacher.name << ",他/她是一名" << (s1.teacher.age > 35 ? "有经验的" : "年轻的") << s1.teacher.gender << "老师。"
<< s1.name << "同学喜欢" << s1.subject.name << "这门课,难度属于" << s1.subject.level << "水平,可以获得" << s1.subject.point << "点学分,这门课现在" << (s1.subject.isOpened ? "已经停止开放了" : "已经开放了") << endl;
// 张三今年18岁了,他/她的班主任是老王,他/她是一名有经验的男老师。张三同学喜欢体育这门课,难度属于中等水平,可以获得3点学分,这门课现在已经开放了
return 0;
}
结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
- 值传递
- 地址传递
示例:
#include <iostream>
#include <string>// 引入string库,用于处理string数据类型
#include <typeinfo> // 引入typeinfo,用于判断数据类型
using namespace std;
struct Student
{
int id;
string name;
int age;
string province;
};
// 类型自动匹配函数
void coutStudent(Student s) {
cout << "姓名:" << s.name << " ";
cout << "年龄:" << s.age << " ";
cout << "学号:" << s.id << " ";
cout << "所在省份:" << s.province << endl;
s.name += "值传递";
s.age += 100;
s.id += 100000;
s.province += "值传递";
}
// 类型自动匹配函数
void coutStudent(Student* s) {
cout << "姓名:" << s -> name << " ";
cout << "年龄:" << s -> age << " ";
cout << "学号:" << s -> id << " ";
cout << "所在省份:" << s -> province << endl;
s->name = (s->name) + "地址传递";
s->age = (s->age) + 100;
s->id = (s->id) + 100000;
s->province = (s->province) + "地址传递";
}
int main() {
Student s1 = { 10001,"张三",14,"江苏" };
// 值传递
cout << endl;
coutStudent(s1);
cout << endl;
cout << "值传递函数执行后的结果为:";
cout << "姓名:" << s1.name << " ";
cout << "年龄:" << s1.age << " ";
cout << "学号:" << s1.id << " ";
cout << "所在省份:" << s1.province << endl;
// 地址传递
Student* p1 = &s1;
cout << endl;
coutStudent(p1);
cout << endl;
cout << "值传递函数执行后的结果为:";
cout << "姓名:" << s1.name << " ";
cout << "年龄:" << s1.age << " ";
cout << "学号:" << s1.id << " ";
cout << "所在省份:" << s1.province << endl;
return 0;
}
结构体中const使用场景
作用:用const来防止误操作
示例:
#include <iostream>
#include <string>// 引入string库,用于处理string数据类型
#include <typeinfo> // 引入typeinfo,用于判断数据类型
using namespace std;
struct Student
{
int id;
string name;
int age;
string province;
};
// 类型自动匹配函数
void coutStudent(const Student* s) {
//s->age = 1000;// 报错
cout << "姓名:" << s -> name << " ";
cout << "年龄:" << s -> age << " ";
cout << "学号:" << s -> id << " ";
cout << "所在省份:" << s -> province << endl;
}
int main() {
Student s1 = { 10001,"张三",14,"江苏" };
// 地址传递
Student* p1 = &s1;
coutStudent(p1);
cout << "值传递函数执行后的结果为:";
cout << "年龄:" << s1.age << " ";
return 0;
}
结构体案例练习
案例1:
共有两个结构体,分别是学生和老师,老师结构体中有姓名和学生数组(结构体嵌套,一共三个老师,每个老师共5名学生);
学生结构体中有姓名和分数;利用函数将老师和对应的学生进行赋值;最后将所有结构体打印出来:
#include <iostream>
#include <string>// 引入string库,用于处理string数据类型
#include <typeinfo> // 引入typeinfo,用于判断数据类型
using namespace std;
// 学生结构体
struct Student
{
string name;
int score;
};
// 老师结构体
struct Teacher
{
string name;
Student student[5];
};
// 对老师进行赋值的函数
void createTeacher(Teacher* t,string tname,Student s[]) {
t->name = tname;
for (int i = 0; i < size(t->student); i++)
{
t->student[i] = s[i];
}
}
// 对学生进行赋值的函数
void createStudent(Student* s,string name,int score) {
s->name = name;
s->score = score;
}
int main() {
Teacher t[3];
// 第一层循环表示的是对老师的赋值
for (int i = 0; i < size(t); i++)
{
string name;
cout << "请输入第" << i + 1 << "个老师的姓名:";
cin >> name;
cout << endl;
Student s[5];
// 第二层循环表示对学生数组进行赋值
for (int j = 0; j < size(s); j++)
{
string stuName;
int score;
cout << "请输入该老师带领的第" << j + 1 << "个学生的信息:" << endl;
cout << "学生姓名:";
cin >> stuName;
cout << endl;
cout << "学生分数:";
cin >> score;
Student* p = &s[j];
createStudent(p, stuName, score);
}
// 学生结构体数组创建完成后,进行老师结构体赋值
Teacher* p = &t[i];
createTeacher(p, name, s);
}
// 打印数据
for (int i = 0; i < size(t); i++)
{
cout << "第" << i+1 <<"名老师是:" << t[i].name << "老师" << endl;
for (int j = 0; j < size(t[i].student); j++)
{
cout << "他的第" << j + 1 << "名学生叫:" << t[i].student[j].name << ",他得分是:" << t[i].student[j].score << endl;
}
}
return 0;
}
案例2:
设计一个人物的结构体,结构体成员有:姓名、年龄、性别;创建结构体数组,数组中存放5名人物
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果
五名人物的信息如下:
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
示例:
#include <iostream>
#include <string>// 引入string库,用于处理string数据类型
#include <typeinfo> // 引入typeinfo,用于判断数据类型
using namespace std;
struct Person {
string name;
int age;
string gender;
};
int main() {
Person p[] = {
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
};
// 冒泡排序算法
for (int i = 0; i < size(p) - 1; i++)
{
for (int j = 0; j < size(p) - 1 - i; j++)
{
if (p[j].age > p[j + 1].age) {
Person tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
// 打印结果
for (int i = 0; i < size(p); i++)
{
cout << "第" << i + 1 << "位人物是:" << p[i].name << ",年龄是:" << p[i].age << ",性别是:" << p[i].gender << endl;
}
return 0;
}