0%

C++ 极速复习

之前我的编程有关的书本网站啥的全部集体吃灰,但是后面报了几个小比赛。然后想极速复习一下之前学的 C++ 知识。废话不多说~

1. 基础

数据类型

(只记有用的!)

int: ±2^31(-2147483648 ~ 2147483647)

double: 10 位有效数字

long long: ±2^63(-9223372036854775808 ~ 9223372036854775807)

unsigned 正数范围加倍,不能存储负数。

(超出范围会溢出,所以,不开 long long 见祖宗

操作符优先级

先乘除后加减

&& > ||

% > + -

&& || > ? :(三元运算符)

实在不会加括号

类型转换

(类型名)待转换的数,比如 (int)1.2

向下取整

其实这是 C 风格,还有 C++ 风格:

static_cast<类型名>(待转换的数)

但是 C yyds!!!

printf scanf

(虽然 C++ 出了 cout cin,但是 printf scanf 又快又方便,所以 C yyds)

它们都在 cstdio 库里。只需要 #include 即可食用。

printf(“输出内容”,待打印项1,待打印项2……)

输出内容中 %d %f 之类的可以替换成待打印项。依次替换。其中 %d 替换整型,%f 替换浮点型,%s 替换字符串。

% 和字母之前可以加点东西,常见的比如加 .数字,比如 %.4d,它规定输出内容必须为 4 位。若输入不满 4 位用前导 0 填充。但如果是 %.4f 浮点型输入,则是小数点后必须为 4 位,若不满 4 位则在其后加 0。

scanf(“输入内容”,待输入项指针1,待输入项指针2……)

待输入项必须为指针,所以前面**必须加 &**。

常见输入内容为读取整型的 %d,浮点型的 %f,但是基本只会用到整型 %d。遇到换行或空格会停止读入。

如一段代码:

1
2
int a, b;
scanf("%d%d", &a, &b);

当输入:

1
3 5

则 a=3,b=5,

当输入:

1
2
3
5

也是 a=3,b=5。

scanf 也有类似的 %.4d 之类的操作,不过不常用。

while

当 while 后所接表达式为真时,继续循环。

(虽然这很弱智,但是之前我真的弱智地以为 while 后面接的是假表达式时才继续循环)

2. 指针与内存

指针初始化

使用 *变量类型名 指针名 = 变量 初始化,如 int *ptr = &a。

初始化之后 ptr 是 a 的内存地址。

解引用

使用 *指针名 解引用,如 *ptr 取得 ptr 所指变量的值。可能要与指针初始化的情况做区分。

取地址

使用 &指针名 取地址,即初始化指针时用的那个 &。特别的,数组无需取地址,例如 int *ptr = arr 即可。

memset

(其实还有 malloc free 或 new delete 创建动态数组的函数,但是其实没啥大用处)

memset 是一个挺牛的函数,它可以批量为数组赋值。**用法为 memset(数组名,需要批量赋值的值,sizeof(数组名))**。memset 也支持为二维数组批量复制。但是 memset 只能批量赋 0 或 -1(据说 ASCII 字符也可以,没试过)。

比如为一个数组 arr 批量赋值 0:

1
2
3
4
5
6
7
8
9
10
11
#include <cstring>
#include <cstdio>

int arr[100];
int main(){
memset(arr,0,sizeof(arr));
for (int i = 0; i < 100; i++){
printf("%d\n", arr[i]);
}
return 0;
}

输出结果全是 0。

3. 函数有关

传参有关

默认的传参形式不能改变实参,因为函数接收到的参数实际上是拷贝过的,作用域的问题。

然后有人在 C 语言里就使用指针传参以达到改变实参的效果,原理简单易懂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <cstdio>

// 一个利用指针传参实现的小交换函数
void swap(int *a, int *b)
{ // 注意区别函数定义的 * 和下面的 *
int temp = *a;
*a = *b;
*b = temp;
}

int main()
{
int a = 3, b = 5;
printf("%d %d\n", a, b);
swap(&a, &b); // 这里要传地址
printf("%d %d\n", a, b);
return 0;
}

当然这种方法被 C++ 的引用传参取代了,虽然并不是很好理解。其实只是换了种写法,原理不变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <cstdio>

// 这是利用引用传参实现的交换函数版本
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}

int main()
{
int a = 3, b = 5;
printf("%d %d\n", a, b);
swap(a, b); // 这里无需传地址
printf("%d %d\n", a, b);
return 0;
}

两者输出结果均是:

1
2
3 5
5 3
内联函数

通常用于短函数,用于提升代码可读性。它的效果是在编译后将函数的代码直接替换至调用处。使用方法直接在函数定义前面加上 inline。这通常用于卡常数(但其实没什么用)

但如果你的编译器不听话的话,它也可以不帮你展开。inline 只是一种建议或提示。

4. 结构体

结构体的使用

由于 OOP 对于 OI 变态的时间限制来说实在太慢,故不复习有关面向对象内容。但是 C++ 中提供了一个类似的东西:结构体。它和类的功能几乎完全相同。

面向对象应该没人不会吧,就跟创建对象一模一样。

1
2
3
4
5
6
7
#include <cstdio>

struct Student
{
int classNum;
int ID;
}; // 分号千万不能少

结构体中也可以定义成员函数,和类一样。

在结构体定义分号前可以直接创建一个实例:

1
2
3
4
5
6
7
#include <cstdio>

struct Student
{
int classNum;
int ID;
} xiaoMing = {3, 5};

或者在其它地方创建实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

int main()
{
Student xiaoMing = {3, 5};
return 0;
}

结构体中也可以轻松访问成员变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

int main()
{
Student xiaoMing = {3, 5};
printf("%d\n", xiaoMing.classNum);
xiaoMing.classNum = 4;
printf("%d\n", xiaoMing.classNum);
return 0;
}
结构体与普通函数配合使用

与上面的代码等价。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

void printClass(Student s)
{
printf("%d\n", s.classNum);
}

void changeClass(Student &s, int num)
{
s.classNum = num;
}

int main()
{
Student xiaoMing = {3, 5};
printClass(xiaoMing);
changeClass(xiaoMing, 4);
printClass(xiaoMing);
return 0;
}
结构体指针

如果你不小心拿到了一个结构体的指针,你可以使用 -> 代替 . 取得该结构体的成员变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

int main()
{
Student *xiaoMing;
xiaoMing->ID = 5; // 它等价于 (*xiaoming).ID = 5
printf("%d\n", xiaoMing->ID);
return 0;
}

运行结果为 5。

5. 其它

sizeof()

是一个长得有点像函数的关键字。它获取变量的存储空间的字节数。它也可以获取数据类型的字节数如 sizeof(int)。

用 sizeof 获取数组的容量时,得到的容量是数组长度 * 数据类型占用的字节数。

1
2
3
4
5
6
7
8
9
10
#include <cstdio>

int arr[100];
int main()
{
// sizeof() 的返回值为 long unsigned int 类型
// 所以如果不想看警告可以手动把它转成 int 类型
printf("%d\n", (int)sizeof(arr));
return 0;
}

输出结果为 400。

用 sizeof 获取结构体的容量时,得到的容量是结构体所有(非静态)成员变量的存储空间的和。

auto

这个东西可以用来代替 int float double 等结构类型,让编译器自动帮你寻找适合的结构类型。比如 auto i = 3 等价于 int i = 3。它主要用于例如 vector<vector> 这样的比较复杂的类型结构之中。

字符串

字符串在 包里,需要 using namespace std;。

字符串的一个冷知识:C++ 中的字符串是 vector 类型。所以 string 可以下标取值,比如 s[3]。

万能头

G++ 编译器里的一个神器:

1
2
#include <bits/stdc++.h>
using namespace std;

需要 using namespace std;。它可以引入 C++ 中的所有库,包括 iostream cstdio vector map 等等非常多。它只存在于 GCC 中,不存在于我之前使用的 MSVC 中,所以一直没发现(T_T)。