一、初识C++

1.1 Theory:

main:程序的入口,每个程序有且仅有唯一一个的函数


1.2 变量:

在C++中,有多种变量类型可用于存储不同种类的数据

变量的名称可以由字母数字下划线字符组成。它必须字母下划线开头

大写字母和小写字母是不同的,因为 C++ 是大小写敏感的

类型描述
bool布尔类型,存储值 true 或 false,占用 1 个字节
char字符类型,用于存储 ASCII 字符,通常占用 1 个字节
int整数类型,通常用于存储普通整数,通常占用 4 个字节
float单精度浮点值,用于存储单精度浮点数。通常占用4个字节
double双精度浮点值,用于存储双精度浮点数。通常占用 8 个字节

1.3 常量:

  1. #define 宏常量

​ Grammar:#define 常量名 常量值;

​ Meaning:通常在文件上方定义,表示一个常量

  1. 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)

代表真或假的值

类型说明本质
ture1
false0

占用空间:1字节

三、运算符

运算符作用
算数运算符用于处理四则运算
赋值运算符用于将表示式的值赋给变量
比较运算符用于比较表示式,并返回一个真或假值
逻辑运算符用于根据表达式的值返回真或假值

3.1 算术运算符

  • 加减乘除:略

    注:整数相除,结果依旧为整数,直接舍弃小数部分,无四舍五入。小数运算得到小数

  • 取模运算(%):求取余数,小数之间不可进行取模运算

  • 前置递增(++?):先让变量+1,然后进行表达式运算

  • 后置递增(?++):先进行表达式运算,然后让变量+1

  • (前/后)置递(增/减):略


3.2 赋值运算符

术语运算符示例运算结果
赋值=a=0a=0a=0
加等于+=a=2;a+=2a=a+2a=4
减等于-=a=2;a-=2a=a-2a=0
乘等于*=a=2;a*=2a=a*2a=4
除等于/=a=2;a/=2a=a/2a=1
模等于%=a=2;a%=2a=a%2a=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;
}

switchif相比

  • 优势:结构清晰,执行效率高
  • 劣势:判断只能是整数或字符型,不可以是一个区间

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 函数的分文件编写

让代码结构更加清晰

  1. 创建后缀名为.h的头文件

  2. 创建后缀名为.cpp的源文件

  3. 在头文件中写函数的声明

  4. 在源文件中写函数的定义

Grammar:#include "xxxx.h"

七、指针

可以通过指针间接访问内存

内存编号从0开始记录,一般以十六进制表示

可以利用指针变量保存地址


7.1 指针变量和普通变量的区别

普通变量存放的是**数据,指针变量存放的是地址**

指针变量可以通过*操作符,操作指针变量指向的内存空间,这个过程被称为解引用

总结:

  1. &获取变量的地址

  2. 利用指针可以记录地址

  3. 对指针变量解引用,可以操作指针指向的内存

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在两侧:指针和内容均不可变(常量指针常量)
  1. 常量指针: - 函数参数传递时,防止函数内部修改数据(如void print(const int* arr);
  2. 指针常量: - 需要固定指针指向的场景(如硬件寄存器地址)
  3. 常量指针常量: - 完全保护数据,适用于全局配置或只读内存

7.4 指针在数组上的应用

数组名本质是首元素地址的常量指针

7.4.1 利用指针访问数组中元素

  1. 声明一个与数组元素类型匹配的指针,并指向数组首地址:

    int *ptr = arr;  // 等价于 int *ptr = &arr[0];
    
  2. 通过指针算术运算直接访问元素:

    // 访问第i个元素(i从0开始)
    int value = *(ptr + i);  // 等价于 arr[i]
    
  3. 通过移动指针逐个访问元素:

    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;
}

注意事项

  • 禁止返回局部变量地址:局部变量在函数结束后被销毁,返回其地址会导致野指针。
  • 必须明确内存管理:若返回堆内存指针,调用者需负责释放(如freedelete)。

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;
}