一、初识C++
1.1 Theory:
main:程序的入口,每个程序有且仅有唯一一个的函数
1.2 变量:
在C++中,有多种变量类型可用于存储不同种类的数据
变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头
大写字母和小写字母是不同的,因为 C++ 是大小写敏感的
| 类型 | 描述 |
|---|---|
bool | 布尔类型,存储值 true 或 false,占用 1 个字节 |
char | 字符类型,用于存储 ASCII 字符,通常占用 1 个字节 |
int | 整数类型,通常用于存储普通整数,通常占用 4 个字节 |
float | 单精度浮点值,用于存储单精度浮点数。通常占用4个字节 |
double | 双精度浮点值,用于存储双精度浮点数。通常占用 8 个字节 |
1.3 常量:
#define宏常量
Grammar:#define 常量名 常量值;
Meaning:通常在文件上方定义,表示一个常量
const修饰的变量
Grammar:const 数据类型 常量名 = 常量值;
Meaning:通常在变量定义前加关键字const,修饰变量为常量
Attention:宏变量不加分号!!(error type:应输入表达式错误)
1.4 标识符命名规则:
标识符不得是关键字
关键字:
C++中预先保留的单词
Tip:给变量或常量命名时,不得使用关键字
标识符由数字、字母、下划线组成
标识符不能以数字开头
标识符中字母区分大小写
Skills:
tab键选中候选词,跳出输入部分
//单行注释
/**/多行注释
vs如何在一个项目中添加多个包含main函数的源文件并分别调试运行
按住找到源文件,右键属性,在窗口中常规一栏中寻找到“从生成中排除”选项,选择“是”
注释: 先CTRL+K,然后CTRL+C
取消注释: 先CTRL+K,然后CTRL+U
二、数据类型
2.1 整形
表示整数
C++中表示整形有以下几种方式,区别在于所占用的内存空间不同
| 数据类型 | 占用空间 | 取值范围 |
|---|---|---|
| short(短整型) | 2字节 | [-215,215-1] |
| int(整型) | 4字节 | [-231,231-1] |
| long long(长长整型) | 8字节 | [-263,263-1] |
sizeof关键字作用:利用
sizeof统计数据类型所占用的内存Grammar:
sizeof(数据类型/变量)
2.2 浮点型
表示小数
| 数据类型 | 占用空间 | 有效数字范围 |
|---|---|---|
| float(单精度) | 4字节 | 7位有效数字 |
| double(双精度) | 8字节 | 15-16位有效数字 |
float数字会被转化位double处理,在数字后加“f”可以阻止这种现象
fixed:取消浮点数的科学计数法
setprecision():控制有效数字的位数注:两者合用控制小数点后的位数
2.3 字符型(char)
显示单个字符
占用空间:1字节
Grammar:char 变量名 = ‘字符值’
说明:字符型变量并不是把字符本身放到内存中储存,而是将对应的
ASCII码放入存储单元
字符型变量对应ASCII码,可以用(int)字符强行将字符转化为十进制ASCII码
常用
ASCII码:A = 65,a = 97
2.4 转义字符
用于表示一些不能显示的ASCII字符
常用转义字符:“\n”表示换行,“\t”表示tab水平制表,“\v”表示垂直制表,“\\”表示“\”
2.5 字符串型
表示一串字符
Grammar:
C风格:
char 变量名[] = “字符串值”; 字符串型变量后面加
中括号[],等号后加双引号,字符型为单引号 以字符数组形式存储,以空字符
\0结尾C++风格:
string 变量名 = “字符串值”; string使用应有头文件:#include <string>
2.6 布尔类型(bool)
代表真或假的值
| 类型 | 说明 | 本质 |
|---|---|---|
| ture | 真 | 1 |
| false | 假 | 0 |
占用空间:1字节
三、运算符
| 运算符 | 作用 |
|---|---|
| 算数运算符 | 用于处理四则运算 |
| 赋值运算符 | 用于将表示式的值赋给变量 |
| 比较运算符 | 用于比较表示式,并返回一个真或假值 |
| 逻辑运算符 | 用于根据表达式的值返回真或假值 |
3.1 算术运算符
加减乘除:略
注:整数相除,结果依旧为整数,直接舍弃小数部分,无四舍五入。小数运算得到小数
取模运算(%):求取余数,小数之间不可进行取模运算
前置递增(++?):先让变量+1,然后进行表达式运算
后置递增(?++):先进行表达式运算,然后让变量+1
(前/后)置递(增/减):略
3.2 赋值运算符
| 术语 | 运算符 | 示例 | 运算 | 结果 |
|---|---|---|---|---|
| 赋值 | = | a=0 | a=0 | a=0 |
| 加等于 | += | a=2;a+=2 | a=a+2 | a=4 |
| 减等于 | -= | a=2;a-=2 | a=a-2 | a=0 |
| 乘等于 | *= | a=2;a*=2 | a=a*2 | a=4 |
| 除等于 | /= | a=2;a/=2 | a=a/2 | a=1 |
| 模等于 | %= | a=2;a%=2 | a=a%2 | a=0 |
3.3 逻辑运算符
作用:用于表达式的值返回真或假值
| 术语 | 运算符 | 示例 | 结果 | 速记 |
|---|---|---|---|---|
| 非 | ! | !a | 输入为假,则结果为真,反之亦然 | 真假颠倒 |
| 与 | && | a&&b | 输入两个真,则结果为真,否则结果为假 | 同真为真,其余为假 |
| 或 | **` | `** | a||b |
四、程序流程结构
C/C++支持最基本的三种程序运行结构:
- 顺序结构:程序按顺序进行,不发生跳转
- 选择结构:依据程序是否满足,有选择地执行某项功能
- 循环结构:依据条件是否满足,循环多次执行某段代码
4.1 选择结构
4.1.1 if语句
执行满足条件的语句
if语句的三种形式:
单行格式if语句:
if (条件) {执行的语句}多行格式if语句:
if (条件) {执行的语句} else {执行的语句}多条件式if语句:
if (条件一) {执行的语句} else if (条件二) {执行的语句} else {执行的语句}嵌套if语句:略
三目运算符
通过三目运算符实现简单判断
Grammar:
表达式1 ? 表达式a : 表达式b;
- 如果表达式1的值为真,执行表达式a,并返回表达式a的结果
- 如果表达式1的值为假,执行表达式b,并返回表达式b的结果
4.1.2 switch语句
执行多条件分支语句
switch (...)
{
case 结果1 : 执行语句; break;
case 结果2 : 执行语句; break;
...
default:执行语句; break;
}
switch和if相比
- 优势:结构清晰,执行效率高
- 劣势:判断只能是整数或字符型,不可以是一个区间
4.2 循环结构
4.2.1 while循环
满足循环条件,执行循环语句
Grammar:while(条件) {循环语句;}
4.2.2 do…while循环
执行循环语句,判断是否满足继续循环条件
Grammar:do{循环语句;} while{循环条件}
与while的区别在于do…while会先执行一次循环语句,再判断循环条件
4.2.3 for循环语句
满足循环条件,执行循环语句
Grammar:for(起始表达式; 条件表达式; 末尾循环体) {循环语句;}
4.2.4 相关语句
break语句:结束分支或循环
continue语句:直接进行下一次循环
goto语句:跳转标记位置
grammar:
A:...... goto A;
不推荐使用,防止造成程序混乱
五、数组
说明:一个集合,存放了相同类型的数据元素
- 数组每个元素都是相同的数据类型
- 数字又连续的内存位置组成
5.1 一维数组
放在一块连续的内存空间中的相同类型的元素
Grammar:
数据类型 数组名[数组长度]数据类型 数组名[数组长度]={值1,值2,..}数据类型 数组名[]={值1,值2,...}
注:数组下标是从0开始索引
数组名用途:
统计整个数组在内存中的长度(
sizeof)获取数组在内存中的首地址
注:
(int)表示将HEX(16进制)转化为DEC(10进制)
&表示取址符号,获取该变量的地址
5.2 二维数组
二维数组本质上是以数组作为数组元素的数组,即“数组的数组”
Grammar:
数据类型 数组名[行数][列数];数据类型 数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};
六、函数
将一段经常使用的代码封装起来,减少重复代码
一个较大的程序,一般分为若干个程序块,每个模块实现特定功能
定义:
返回值类型 函数名 (参数列表)
{
函数体语句
return表达式
}
返回值类型:
定义了函数执行后返回给调用者的数据类型。返回值可以是任何数据类型,包括基本数据类型(如int、float)、复合类型(如数组、结构体)、指针、引用,甚至是void类型,表示函数不返回任何值
总结:
无参数括号为空,有参数括号内写参数
返回值类型为void就不需要return
| 有参 | 无参 | |
|---|---|---|
| 无返 | 用void,括号写参数 | 用void,括号空 |
| 有返 | 不用void,括号写参数 | 不用void,括号空 |
声明为提前告知函数存在,防止报错 声明可以多次,定义只有一次
6.1 值传递
将实参的值完整复制一份给形参
- 内存操作:形参是实参的独立副本,函数内操作形参不影响原始数据
- 适用场景:基本数据类型(
int,double等)、小型结构体
void swap(int a, int b) { //函数定义里的小括号为形参
int temp = a;
a = b;
b = temp; // 仅交换副本,不影响实参
}
int main() {
int x = 10, y = 20;
swap(x, y); //函数调用时传入的参数为实参
cout << x << " " << y; // 输出 10 20(未交换)
return 0;
}
特点:
- 安全:函数无法修改原始数据
- 性能开销:复制大型对象(如类实例)时效率低
6.2 指针传递
将实参的内存地址传递给指针形参
- 内存操作:通过解引用(
*)直接修改原始数据- 适用场景:需要修改实参、传递数组或大型对象
void swap(int *a, int *b) {
int temp = *a;
*a = *b; // 通过地址修改原始数据
*b = temp;
}
int main() {
int x = 10, y = 20;
swap(&x, &y); // 传递地址
cout << x << " " << y; // 输出 20 10(成功交换)
return 0;
}
特点:
- 显式操作:需使用取地址符
&和解引用符* - 灵活性:可以修改指针指向的地址(但需注意野指针问题)
- 潜在风险:可能操作空指针或无效地址
6.3 引用传递
形参是实参的别名,直接绑定到原始变量
- 内存操作:无需复制数据,操作形参即操作实参
- 适用场景:需要修改实参、传递大型对象(如类、容器)
void swap(int &a, int &b) {
int temp = a;
a = b; // 直接修改原始数据
b = temp;
}
int main() {
int x = 10, y = 20;
swap(x, y); // 直接传递变量
cout << x << " " << y; // 输出 20 10(成功交换)
return 0;
}
特点:
- 语法简洁:无需
&和*,形参直接操作实参 - 安全性:引用必须初始化,且不可为空
- 高效性:避免复制大型对象(如
std::vector)
6.4 对比总结
| 特性 | 值传递 | 指针传递 | 引用传递 |
|---|---|---|---|
| 传递内容 | 值的副本 | 内存地址 | 变量的别名 |
| 内存开销 | 高(复制数据) | 低(仅地址) | 低(无复制) |
| 语法形式 | void func(int a) | void func(int *a) | void func(int &a) |
| 调用方式 | func(x) | func(&x) | func(x) |
| 修改实参 | 不可 | 可(通过*a) | 可(直接操作) |
| 空值风险 | 无 | 有(需检查空指针) | 无(必须绑定有效变量) |
| 适用场景 | 基本类型、小型数据 | 需显式操作地址、兼容C代码 | 修改实参、传递大型对象 |
6.5 常见误区
误用指针和引用:
int *a; // 未初始化的指针(野指针) int &b; // 错误!引用必须初始化混淆指针和引用的行为:
void func(int *p) { p = nullptr; } // 修改指针副本,不影响实参指针 void func(int &r) { r = 10; } // 直接修改实参
通过理解C++中三种参数传递方式的区别,可以更高效地管理内存、提升性能,并避免因错误操作导致的数据损坏或程序崩溃
6.6 函数的分文件编写
让代码结构更加清晰
创建后缀名为.h的头文件
创建后缀名为.cpp的源文件
在头文件中写函数的声明
在源文件中写函数的定义
Grammar:#include "xxxx.h"
七、指针
可以通过指针间接访问内存
内存编号从
0开始记录,一般以十六进制表示可以利用指针变量保存地址
7.1 指针变量和普通变量的区别
普通变量存放的是**数据,指针变量存放的是地址**
指针变量可以通过*操作符,操作指针变量指向的内存空间,这个过程被称为解引用
总结:
&获取变量的地址利用指针可以记录地址
对指针变量解引用,可以操作指针指向的内存
p:地址,*p:值
指针所占内存空间
Q:指针也是种数据类型,那么这种数据类型占用多少内存空间?
A:在C++中若是32位操作系统,所有指针类型都占4字节
在64位操作系统中,int型占4字节,其他8字节
7.2 空指针和野指针
| 行为 | 野指针 | 空指针 |
|---|---|---|
| 定义 | 指向已释放内存的指针 | 明确指向无内存地址(0) |
| 解引用操作 | 未定义行为(可能崩溃) | 明确崩溃(可被调试捕获) |
| 安全性 | 高风险 | 低风险(易于检查和处理) |
7.3 常量指针和指针常量
| 类型 | 语法 | 指向地址 | 指向值 |
|---|---|---|---|
| 常量指针 | const int* p | 变量 | 常量 |
| 指针常量 | int* const p | 常量 | 变量 |
| 常量指针常量 | const int* const p | 常量 | 常量 |
记忆技巧
const在*左侧:修饰数据类型,指向内容不可变(常量指针)const在*右侧:修饰指针本身,指针不可变(指针常量)const在两侧:指针和内容均不可变(常量指针常量)
- 常量指针: - 函数参数传递时,防止函数内部修改数据(如
void print(const int* arr);)- 指针常量: - 需要固定指针指向的场景(如硬件寄存器地址)
- 常量指针常量: - 完全保护数据,适用于全局配置或只读内存
7.4 指针在数组上的应用
数组名本质是首元素地址的常量指针
7.4.1 利用指针访问数组中元素
声明一个与数组元素类型匹配的指针,并指向数组首地址:
int *ptr = arr; // 等价于 int *ptr = &arr[0];通过指针算术运算直接访问元素:
// 访问第i个元素(i从0开始) int value = *(ptr + i); // 等价于 arr[i]通过移动指针逐个访问元素:
for (int i = 0; i < 5; i++) { printf("%d ", *ptr); // 输出当前元素 ptr++; // 指向下一个元素 }
7.4.2 利用指针修改数组元素
通过解引用指针直接修改值:
*(ptr + 2) = 100; // 修改第3个元素(arr[2] = 100)
7.5 指针在函数上的应用
7.5.1. 指针作为函数参数
作用:通过传递地址,允许函数直接修改外部变量或高效传递大型数据。
示例:交换两个变量的值
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
swap(&x, &y); // 修改实参x和y的值
printf("x=%d, y=%d", x, y); // 输出x=20, y=10
return 0;
}
特点:
- 避免数据复制:传递大型结构体或数组时,仅传递地址(如
void process(int *arr, int size))。 - 允许修改外部变量:通过解引用操作直接操作内存。
7.5.2. 指针作为函数返回值
作用:返回动态分配的内存地址或特定数据结构的指针。
示例:返回动态数组
int* createArray(int size) {
int *arr = (int*)malloc(size * sizeof(int));
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
return arr; // 返回堆内存指针
}
int main() {
int *arr = createArray(5);
printf("%d", arr[2]); // 输出20
free(arr); // 必须手动释放内存
return 0;
}
注意事项:
- 禁止返回局部变量地址:局部变量在函数结束后被销毁,返回其地址会导致野指针。
- 必须明确内存管理:若返回堆内存指针,调用者需负责释放(如
free或delete)。
7.5.3. 函数指针(指向函数的指针)
作用:将函数作为参数传递或实现回调机制。
语法:返回值类型 (*指针名)(参数列表)
示例:通过函数指针调用不同操作
#include <stdio.h>
// 定义函数指针类型
typedef int (*MathFunc)(int, int);
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
// 函数指针作为参数
void calculate(MathFunc func, int x, int y) {
printf("结果: %d\n", func(x, y));
}
int main() {
MathFunc func_ptr = add;
calculate(func_ptr, 5, 3); // 输出8
calculate(subtract, 5, 3); // 输出2
return 0;
}
应用场景:
- 回调函数:如事件处理、排序算法中的比较函数(
qsort)。 - 动态调用逻辑:根据条件选择不同函数执行。
4. 函数返回指向函数的指针
作用:根据条件返回不同的函数实现。
示例:根据操作符返回对应函数
#include <stdio.h>
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
// 返回函数指针
int (*getOperation(char op))(int, int) {
switch (op) {
case '+': return &add;
case '-': return &subtract;
default: return NULL;
}
}
int main() {
int (*operation)(int, int) = getOperation('+');
if (operation) {
printf("结果: %d", operation(5, 3)); // 输出8
}
return 0;
}
5. 函数指针数组
作用:批量管理多个函数,便于统一调用。
示例:命令模式或菜单驱动程序
#include <stdio.h>
void start() { printf("程序启动\n"); }
void stop() { printf("程序停止\n"); }
void pause() { printf("程序暂停\n"); }
int main() {
// 定义函数指针数组
void (*commands[])() = {start, stop, pause};
int choice;
printf("输入选项 (0-启动, 1-停止, 2-暂停): ");
scanf("%d", &choice);
if (choice >= 0 && choice <= 2) {
commands[choice](); // 调用对应函数
}
return 0;
}
八、结构体
结构体属于用户自定义的数据类型,允许用户储存不同的数据类型
是不同数据类型的复合体
8.1 结构体的定义与使用
struct 结构体名 变量名struct 结构体名 变量名 ={成员1值, 成员2值, ...}
定义结构体的关键字是
struct,不可省略创建结构体变量时,关键词
struct可以省略结构体变量利用操作符
.访问成员
8.2 结构体数组与结构体指针
将结构体放入数组中方便维护
Grammar: struct 结构体名 数组名[元素个数]={{}, {}, ...}
//结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
}
int main() {
//结构体数组
struct student arr[2]=
{
{"张三",18},
{"李四",19},
};
for (int i = 0; i < 2; i++)
{
cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << endl;
//通过操作符“.”访问成员
}
system("pause");
return 0;
}
通过指针访问结构体中的成员
利用操作符->可以通过结构体指针访问结构体属性
//结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
};
int main() {
struct student stu = {"张三",18};
struct student *p = &stu; //建立结构体指针
p->score = 80; //指针通过“->”操作符可以访问成员
cout << "姓名:" << p->name << " 年龄:" << p->age << endl;
system("pause");
return 0;
}
8.3 结构体嵌套结构体
结构体中的成员可以是另一个结构体
struct student //学生结构体定义
{
string name; //姓名
int score; //分数
};
struct teacher //教师结构体定义
{
int id; //职工编号
string name; //教师姓名
struct student stu; //子结构体 学生
};
int main() {
struct teacher t1;
t1.id = 10000;
t1.name = "老王";
t1.stu.name = "张三";
t1.stu.score = 100;
cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << endl;
cout << "辅导学员 姓名: " << t1.stu.name << " 考试分数: " << t1.stu.score << endl;
system("pause");
return 0;
}
8.4 结构体做函数参数
将结构体作为参数向函数中传递
总结:如果不想修改主函数中的数据,用值传递,反之用地址传递
8.5 结构体中const使用场景
用const防止误操作
//const使用场景
void printStudent(const student *stu) //加const防止函数体中的误操作
{
//stu->age = 100; //操作失败,因为加了const修饰
cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;
}