C++ 期末复习 2024
本科生学业指导中心
逸夫楼 201
王尧勇
哔哩哔哩 王尧勇
计算机科学与工程学院 2020级 计算机科学与技术专业
0. 前言
编程语言是人类定义的规则,如果制定 C++ 规则的人的思维是正常的,那么 Ta 的这些规则大抵应该是符合逻辑的。
于是我们可以从规则制定者的角度去审视编程语言的语法,即设计这个东西出来是用来干什么的?
这个语法可以做到之前的语法做不到的事情(比如结构控制语句)。
这个语法可以更加方便地做到之前语法做起来很麻烦的事情(比如函数)。
这个语法是为了弥补之前某个语法造成的问题。
1. 基本类型和变量
1.0 关键字和标识符
关键字就是语言本身要用到的一些名字,本身表示了某种功能,因此不能再用它们来命名。
标识符,就是某个东西的名字,最常见的就是变量的命名,遵循以下规则:
字母、数字、下划线 组成
数字不能作为第一个字符
不能和关键字一样
注意,关键字严格按照课本所列举的记忆,
cin、cout不是关键字,define、include不是关键字,等等。
1.1 基本类型
5种基本类型:布尔型(bool)、字符型(char等)、整型(int等)、浮点型(double等)、空类型(void)。

类型,就是对内存中的 01 串的不同角度的诠释。
布尔型
布尔型直接
cout会输出数字,如果要输出true或false,则cout << boolalpha <<
布尔型转整型:
true为1,false为0整型转布尔型:
0为false,非0为true
整型
整数罢了,但是存的都是补码的形式

取反加一?都什么年代,还在算传统补码😋
例如,对于 8 位的整型,-3 在内存中是以补码存储,也就是 256 - | -3 | = 253 = 1111 1101
浮点型
浮点数存储原理和整数很不一样(IEEE 754)
存在误差,可能十进制下的有限小数在二进制下是无限循环的,因此可能会有精度的损失
因此判断一个浮点数是否等于某个数不能直接用 == 判断
字符型
字符型在内存中就是按照整数存储的
整数和字符的映射规则就是 ASCII 码
课本中和考试中,
char都是默认有符号的,因此,如果你见到一些故意找茬的赋值语句:
1.2 字面值
字面值的含义就是其字面意义:字面上的值😅
整型值的不同进制表示:无前缀默认为10进制、数字0开头表示8进制、0x或者0X开头表示16进制、0b或0B开头表示2进制。
浮点值的定点表示法:不加后缀默认为
double、加后缀f或F为float、加后缀l或者L为long double浮点值的科学表示法:
尾数e指数或尾数E指数,注意 e 后面只能是整数转义字符
转义字符的主要目的是为了表示键盘上不方便敲进代码的键位,例如
‘\n’、‘\t’等另外转义字符也可以用ASCII码来表示字符,但只能用8进制或16进制,8进制0开头例如
'\023'、16进制x开头例如'\xa'
转义字符的功能超乎你想象
1.3 变量
变量初始化
auto自动推导
const命名常量
2. 运算符和表达式
2.0 杂项
表达式没有分号,有分号的是语句捏
运算符优先级
考前背就完事了,真记不住所有的
优先结合并非优先计算
表达式的返回值
2.0 算数运算符
单目运算符:
-负数运算符,+正数运算符;+加、-减、*乘;/除:除数不能为 0,整型和整型运算结果为整型;浮点数运算,结果为浮点数;
a%b取模:b 不能为 0,结果的符号和 a 一致,运算变量不能是浮点数
开平方使用
sqrt函数(cmath或math.h头文件),返回值类型为 double;没有数学上的 $a^b$ 运算符,可以使用
cmath中的pow函数,不要用^,这是异或捏😫
2.1 关系运算符
6个关系运算符:
<、<=、>、>=、==、!=;结果为布尔类型
当两个参与比较的左右操作数类型不同时,自动转换为更高精度的类型再比较
==不要写成=😅
2.2 逻辑运算符
3个逻辑运算符:
!逻辑非、&&逻辑与、||逻辑或;!的优先级高于算数运算符,&&和||的优先级低于算术运算符和关系运算符;&&和||的短路特性
不考虑优先级?🧐
2.3 条件比较运算符
条件运算符
<表达式1>为真则求解<表达式2>,其值作为整个式子的返回值,<表达式3>不会执行
<表达式1>为假则求解<表达式3>,其值作为整个式子的返回值,<表达式2>不会执行
比较前,当<表达式2>和<表达式3>类型不同时,自动转换为更高精度的类型
2.4 位运算
<<左移:右边补0,带符号数溢出可能会改变符号
>>右移:带符号数左边补符号数,无符号数左边补0若移动位数为负数,VS和DEV C++会警告,最后结果为0
移动位数不要超过变量的位数,否则按取模算
~取反、&按位与、|按位或、^按位异或优先级:取反 > 按位与 > 按位异或 > 按位或
操作数必须是整型
位运算均针对补码
2.5 赋值运算符
优先级很低(仅高于逗号运算符)
结合性自右向左:
a=b=c;相当于a=(b=c);赋值运算符左边必须是左值,赋值运算符的返回值也是左值,即可以
(a=5)=6;
2.6 逗号运算符
优先级最低🤣🤣🤣
从左到右计算,最右边的表达式作为整个式子的值
若最右边是一个左值,逗号表达式的值也是左值
坑😡
2.7 自增自减运算符
以自增运算为例。
前置 i++
+1
返回 变量i 的值
后置 ++
j = ii = i+1返回
j
++的优先级高于所有算术运算符,且后置高于前置
++必须是左值,前置++返回左值,后置++返回右值
自增和加法一起用容易混淆,甚至引发错误,不要这么干
同时自增运算很容易出现未定义行为
2.8 sizeof
sizeof是一个运算符,不是一个函数!sizeof(类型名)或sizeof(表达式),前者编译时计算完毕,后者运行时计算;sizeof(指针)得到的是指针变量的大小,例如32位系统大小为4,64位系统大小为8;sizeof(数组名)得到的整个数组所占字节数,再除以sizeof(数组元素类型)可得数组元素个数;
2.9 类型转换

整型提升
强制类型转换
类型转换可以深究很多,好在考试不会这么难
2.10 typeid、 decltype
typeid、 decltype3. 基本语句
3.0 if
if3.1 switch
switch如果正常人写出来的
switch语句,不会有什么难度,然而考试考的都是不正常的😅case中无breakdefault不在最下面
case和default只决定从哪里开始执行,一旦确定之后,便是从上往下执行,直到执行完或者遇到break
3.2 while
while3.3 for
for什么B题目啊😅
3.4 跳转语句
breakcontinuegoto
4. 函数
4.0 函数和主函数
main函数的参数
4.1 值传递、引用传递、指针传递
值传递
为什么没有交换,怎么会事捏?🤨
引用传递
指针传递(地址传递)
指针传递的坑
4.2 函数重载
讨论函数重载时,把自己想象为编译器,
可爱的小编译器一枚吖😘形参个数和类型不同就可以重载
核心在于让编译器知道是两个不同的函数,在传入参数的时候不会混淆
返回值类型不同不能用来重载
有默认值的情况可能会有干扰:
const修饰不能作为重载的依据(仅限这种最简单的情况下,涉及到指针和引用的const就复杂了)
重载函数调用时的最佳匹配:
严格匹配
整型提升
赋值转换
有多个形参时情况复杂,核心还是没有二义性;
函数重载的类型匹配很复杂,好在考试不会涉及到更复杂的(应该吧)
4.3 默认参数
必须优先保证右边的参数有默认值,不能出现左边有,而右边没有的情况
4.4 变量作用域
每当使用变量时,使用的是最近的还存活着的那个变量
4.5 变量类型
平平无奇的非静态局部变量
静态局部变量:生命周期是全程的,但只能在函数内使用
孟婆汤掺水了
静态全局变量:单个文件内部共享
非静态全局变量:多文件共享
extern修饰
4.6 函数的递归
函数可以调用函数,函数可以调用自己
盗梦空间是吧
4.7 可变参数
4.8 constexpr 函数
constexpr 函数编译时期确定值
只要在编译时、运行前能知道函数的结果,那就不会报错
5. 编译预处理
宏命令末尾没有分号
5.0 文件包含
#include <filename>或者#include "filename"纯纯的复制的效果
5.1 无参宏
作用域是定义的那一行以下直到文件末尾
宏定义和变量重名
子字符串不会被替换
5.2 有参宏
十分机械的字符串替换
6. 结构体、枚举、联合体
6.0 结构体
在 C++ 中,结构体和类唯一的区别就是默认的访问修饰符不一样,结构体默认
public,类默认private,除了这一点,其他的特性完全一样。常见考题
6.1 结构体的字节对齐
6.2 结构体的嵌套定义
不能嵌套定义自己
但是嵌套自己的指针可以(链表的节点)
6.3 位域
6.4 枚举
枚举类型的定义
不指定值,其值就是前一个+1
指定值,就是指定的值
第一个如果不指定值,那就是 0
枚举变量的定义
枚举 和
int的转换枚举常数会隐式转换成
int,可以把枚举值赋值给int变量但是反过来不会,所以不能把一个
int值赋值给枚举变量枚举值参与算术运算时隐式转换为
int参与运算,结果也不再是枚举类型
典中典的一道题
6.5 强类型枚举
6.6 联合体
百变怪罢了
6.7 typedef 和 using
typedef 和 using7. 指针
7.0 一些说明
学习指针,至少从备考的角度来说,要更多的关注 怎么用,而非更多关注 是什么
说明几个概念,下文中都是表示如下意思:
指针:指的是各种指针变量
地址:指的是
0x12356ff这样的值,指针里面存的就是地址
学习一个指针(或者一个类似指针的东西,比如数组名),我们只要关心其以下几个特点:
级别
指向类型
自身指向的可变性
指向的内容的可变性
7.1 内存和地址
指针相关的题,画好图就可以帮助解题
7.2 指针和多级指针
为什么需要指针?
为了考试难灵活,太灵活了
动态开辟内存
控制数组、控制函数、控制变量
跨作用域
指针为什么分这么多不同类型?
在语法层面进行区分,不然都是
void*不好读指针加减法时的实际地址变化不一样
指针的定义
多级指针
套娃思想
指针其实就是个用来存地址的变量,那么指针变量本身也有对应的内存地址
变量的地址可以被指针变量存储,即指针可以指向一个普通变量,那么也可以有一种变量用来存储指针变量的地址,即二级指针
7.3 数组
数组定义
数组大小只能为正整数常量(包括const常量、C++11中
constexpr函数),实际使用时发现编译器也允许用变量定义数组大小,但这不好🥵数组初始化
对部分元素初始化,后面没初始化的默认为0
对所有元素赋值,可不指定长度,根据所给个数确定
定义时没初始化,之后就不能用大括号初始化了
访问数组时注意下标为
0~n-1,下标越界语法上没有错误,会访问到未知的内存
数组和数组不能整体赋值
不要直接比较数组名来判断两个数组是否相等(实际上在比较地址)
以下方式对str的声明中,不能将str作为字符串使用的是:
二维数组
二维数组可以看成一维数组的数组
二维数组的初始化:核心:列宽
实际存储是一维的
7.4 指针和数组
数组在很多时候,表现得像一个指针,但是它不是指针,不是指针
之所以这样,是很多时候我们使用数组的时候,它很容易就隐式转换成指针类型了

注:本人仅使用这张图代表的梗来和数组与指针类比,达到一种幽默诙谐的教学作用,并不代表本人认同或者不认同原图代表的观点和言论。
数组是一个常量,值不能被改变,值不能被改变,值不能被改变
一级指针可以指向一个数组,就是指向数组首元素,把一个数组赋值给指针,是隐式转换为了常量指针赋值给指针
下标访问 和 解引用 等价
当然了,还是有点差别,这俩优先级不一样,
[]优先级高于*解引用和自增自减运算
若有int a[]{1, 3, 5, 7, 9}, *p = a+2;,则执行表达式int b = 1 + (*p++);后,b和*p的值为多少?
A. 6 6 B. 6 7 C. 7 6 D. 8 7
二维数组也同样表现得像一个二级指针常量,其指向不能被改变
上面提到可以把数组视作指针常量来做题,但在以下情形中,二者有区别
sizeof运算符&取地址符
7.5 指针数组和数组指针
7.5.0 指针数组
数组的元素是丰富多彩的,可以是基本类型。可以是结构体,可以是对象,那当然也可以是指针
指针数组 就是一个普通的数组,只不过它的每个元素都是指针类型
而数组本身又表现得像一个常量指针,故它可以看作和二级指针同级别
7.5.1 数组指针
是一个指针
指向数组的指针
用于指向一个特定列宽的二维数组,其级别和二维数组相似

在数组指针指向p之后,即可用p访问二维数组a
若有
则++*(*++p)的值为多少?
7.6 二级指针总结
int** p;
Y
Y
int a[2][3];
N
N
int (*p)[4];
Y
N
int *b[4];
N
Y
7.7 字符数组和字符串
C++基本类型中没有字符串,用字符数组或字符指针的形式来表示字符串(当然还有string)
第一行是定义了一个字符数组,可以存字符串,最大长度为10(包括了最后的
\0)第二行是定义了一个二维数组,也可以看做一个字符串的一维数组,可以存4个字符串,每个字符串最大长度是20(包括了最后的
\0)字符数组的初始化
7.8 字符指针
字符指针本身只有一个指针变量的大小,可以指向一个常量字符串
或者指向一个已有的字符数组
7.9 字符数组和字符指针


字符二维数组和字符指针数组

若有char *s1 = "C++";和char s2[] = "C++";,则下面选项不会导致错误的是:
7.10 字符串的输入输出
cin或者cin.get,利用循环,一个字符一个字符读入,其中cin会跳过空格一类的字符,cin.get可以读取空格、制表符、换行符;cin>>str;直接将一个个字符读入str所在内存,末尾自动加\0,cin会被空格、制表符、换行符截断;cin.getline(str, 10);读入字符串,可读入空格、制表符,遇到回车结束;只有指向变量的指针或者数组名才可以输入;
cout << 字符指针或字符数组,从给定位置一直输出字符直到遇到\0;
7.11 字符串处理函数
字符串处理函数在头文件
<cstring>(<string.h>)中;strlen(str),返回int类型的字符串长度(不包括\0),str可以使字符数组,也可以是字符指针,也可以是字符串常量;strcpy(s1, s2),返回值指向第一个参数所指位置的字符指针,从位置s1开始,把s2开始的字符串一个一个拷贝过来,s1必须是指向可修改的变量内存的,注意数组越界问题
strcat(s1, s2),返回指向第一个参数所指位置的字符指针,将字符串s2拼接到s1后面,s1必须是指向可修改的变量内存的,注意数组越界问题;strcmp(s1, s2),返回一个int整数,从前往后逐个位置比较s1和s2中对应位置的字符的ASCII码的值,若s1大于s2,则返回值大于0,若s1小于s2,则返回值小于0,相等则返回值为0;VS中提供了
strcpy_s、strcat_s的安全版本;
7.12 指针与函数
7.12.0 指针作为函数形参
指针作为函数形参可以在不同函数之间传递变量
一维数组名作为函数形参,会退化为一级指针
一级指针等价于一维数组
二级指针等价于指针数组
数组指针等价于二维数组
下面哪一个函数能接受处理任意行、任意列的int矩阵,row表示行数,col表示列数:
若有
1.以下选项不能作为实参调用函数void f(int (*p)[4])的是:
A. &a
B. &b
C. &c[0]
D. c
2.以下选项能作为实参调用函数void f(int **)的是:
A. &a
B. &b
C. c
D. d
7.12.1 指针作为函数返回值
不能返回局部非静态变量
返回外部传进来的指针
返回局部静态变量
返回全局变量
返回动态开辟的内存
7.12.2 函数指针
函数指针用于指向某一类函数
typedef或using起别名
设有语句
下面哪一个定义了返回值为int型指针的函数?
7.13 动态分配内存 new 和 delete
new 和 deletenew返回一个指向开辟出的内存的指针,若开辟了一个数组,则是指向首元素的指针
下面哪一条语句是正确的?
若创建的是二维数组,则返回值类型为数组指针
new出来的内存在堆上,不在当前函数的栈内存上,不会自动回收delete可以手动删除new出来的指针
7.14 const 指针、void 指针、空指针、野指针
const 指针、void 指针、空指针、野指针7.14.0 const 指针
const 指针const默认修饰左边,除非左边没有或者左边已经被修饰
const指针的重载问题:之前在函数重载中提到,const不能作为重载的依据,即一个形参变量是不是const修饰,不作为形参的区别,但是指针这里有例外
7.14.1 void指针
void指针void指针可指向任何类型的变量、函数
通过强制类型转换,void指针可以控制许多变量,进行通用性设计
7.14.2 空指针和野指针
指针可以不指向任何地方,赋值为
nullptr即可(或NULL)野指针:被
delete了的指针(这种也叫 悬垂指针)或者没有初始化赋值的局部变量指针,我们不知道它指向了哪里🤨
8. 引用
8.0 左值引用
引用就是别名
可以把引用看作功能阉割的指针,灵活性减小,但是使用更加方便了,但引用不是指针
之后对
ra的所有操作就相当于对a的操作const修饰的左值引用可以引用字面值或者右值表达式
8.1 引用与函数
引用作为函数形参,可以传递变量
引用作为函数返回值,和指针作为函数返回值类似``
返回动态开辟的内存的引用,注意用完之后
delete
函数的引用
函数引用可作为函数的返回值
8.2 引用与数组
和指针不同的是,数组的元素不能是引用
可以有数组的引用,但数组的引用不能作为函数的返回值
8.3 引用与指针
引用的指针就是被引用变量的指针
指针的引用
没有引用的引用,芝士右值引用
没有空引用
8.4 & 符号
& 符号到目前为止,已经接触了 & 的三种用法,注意不要混淆
&在位运算中代表按位与&表示取地址操作&可以用来定义 左值引用
9. 链表
链表结点的结构:存值的变量,存后继的指针
处理链表的函数中,通常都是会判断是否是头结点,并做特殊处理,或者判断头结点是否为空;
通常需要两个指针来处理链表,一个用来指向新的结点,一个用来指向链表的某个位置;
遍历链表时,通常会出现
p = p->next;这样的语句;
1.已知一个单向链表结点的数据结构为:
函数Node* AddNode (Node *head, int n)的功能是:新建结点使之数据为n,将该结点处插入到head指向的单向链表中,放在数据与n差距最小的结点(如有多个最小差距结点,则考虑第一个)之后,并返回链头结点地址。若为空链,则新建结点直接作为头结点。
在程序空格位置填入合适的代码,完成AddNode函数。
2.在精准扶贫的大背景下,在政府和企业的帮助下,小明开了个养猪场。该养猪场有若干猪圈,我们用一个单向链表用于保存猪圈中猪的相关信息,其结点的数据结构为:
函数double Sell(Pig *&head, double threshold, double price)的功能是:将体重超过threshold斤的猪出笼,以price元/斤的价格售出并返回总售价。具体操作为:将链表中weight超过threshold的结点全部删除,同时根据price元/斤的价格计算并返回总售价。
在程序空格位置填入合适的代码,完成Sell函数。
Last updated