补充
本章对一些知识进行补充说明,这些知识在语法基础阶段应用场景不多,所以在前面为了降低理解难度并没进行讲解,在此处进行列举。
1.变量类型补充
整数类型在定义时,可以使用 signed
和 unsigned
关键字进行修饰。
signed
表示带符号整数(默认),这个用的少,因为默认就是带符号的。
unsigned
表示无符号整数,这样定义出来的整数变量无法存储负数。
例如 unsigned int
unsigned long long
,相较于带符号整数,无符号整数腾出了一个符号位,所以能存更大的数字。
2.C风格的输入输出
scanf
和 printf
其实是C语言提供的函数,大部分情况下它们输入输出的速度快于 cin
和 cout
。
在使用时需要配合占位符来使用,先看例子:
#include <bits/stdc++.h>
using namespace std;
int main() {
int x;
double y;
scanf("%d %lf", &x, &y); // 输入
printf("x: %d y: %lf", x, y); // 输出
return 0;
}
在这段代码中,%?
即是一个占位符,其意思是在此处会 输入/输出 一个对应类型的数据,根据 %
后面的内容不同,代表的数据类型也不同,下面进行了说明:
%d
: 代表是int
%c
: 代表是char
%lf
: 代表是double
%lld
: 代表是long long
%s
: 代表是字符串,但注意不能直接输入到string
,可以直接输入到char[]
数组中。
在输入输出文本中,除了占位符外,可以加入一些自定义的文本,在一些规定了格式的输入输出的场景下,scanf
和 printf
代码写起来会更简单。
scanf
中的 &
是什么?
在这里,&
是取址运算符,即获取变量在内存中的地址,不用过于深究,记住这个区别即可。
另外,占位符还可以控制输出的格式,例如保留小数位数,位宽等等,下面例子进行了一些常用的:
printf("%.3lf\n", 3.14159); // 保留三位小数 \n为换行
printf("%3d", 12); // 设置位宽为3,不足的会在前面补空格
printf("%03d", 12); // 设置位宽为3,不足的会在前面补0
还有更多用法可以自行探索。
3.#define 命令
#define
是一种预处理命令,用于定义宏,本质其实是用一个文本替换另一个文本,例如:
#include <bits/stdc++.h>
#define qwq cout << "hello"
using namespace std;
// 这里相当于会把代码中所有独立的 qwq 文本替换为 cout << "hello"
// 但如果是非独立的文本则不受影响,例如 aqwq 是不会被影响的。
int aqwq = 5;
int main() {
qwq;
cout << '\n' << aqwq;
return 0;
}
#define
使用起来是有风险的,因为其作用域是整个程序,可能会导致一些粗心的错误,较为常见的一个用法是:
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
...
return 0;
}
因为大部分 oier 都习惯了 int
,所以这样可以避免忘记开 long long
导致的错误。但随之而来的是可能会增大常数导致 TLE
,或者因为爆空间而 MLE
,所以使用时要自己能算清楚时间复杂度空间复杂度之类的。
4.三目运算符
三目运算符是 if else
的一种简写,常用于简单的条件判断,特别是需要根据条件给变量赋值时。
语法格式为: 判断条件 ? 成立的情况 : 不成立的情况
例如:
cout << (2 > 1 ? "yes" : "no") << '\n'; // 会输出 yes
int x, y, z;
z = (x > y) ? y : x; // 如果 x>y,那么 z=y,否则 z=x
初赛的程序阅读可能会出现,所以还是了解一下这个执行逻辑,自己写代码的时候没必要用。
5.加快输入输出
在数据量级较高的情况下(输入 scanf
和 printf
的效率要比 cin
和 cout
的效率高 scanf
读入字符数据时,会需要注意 空格 和 换行的影响,处理起来会比较麻烦,所以建议大家用以下方式:
int main() {
// 这行代码作用是关闭输出流,取消c风格的输入输出和c++风格输入输出的
// 同步,写了之后注意两者不要混用。
ios::sync_with_stdio(false);
// 这两行是把 cin 和 cout 取消关联,进行输入操作时不再强行刷新缓冲区。
cin.tie(0);
cout.tie(0);
...
后面正常用cin cout 读写数据
}
加上以上三行代码后,cin
、cout
的效率和C风格的相差无几,甚至是快一些的,但使用后要注意以下几点:
-
使用后就不要用
scanf
和printf
了,因为同步流已经关闭了,全用cin
和cout
即可。 -
换行时写
cout << '\n'
,不要用endl
,因为endl
通常会刷新缓冲区,但使用上述代码后它不再具备刷新的功能。 -
这段代码的作用只是提高输入输出的效率,对于程序的结果不会任何影响。
6.初始化数组中的值
众所周知,数组定义在全局时,默认里面的元素都为
但如果在某些场景下,需要把数组中的初始值都设置为其它值的时候,怎么做呢?容易想到的是直接手写一个循环:
for (int i=1; i<=n; i++) a[i] = 1; // 都设置成 1
利用 memset
函数可以达到相同的效果:
memset(a, 0, sizeof a); // 批量设置为 0
不过 memset
并不能随意批量设置值,中间的数据在赋值时会被转为 unsigned char
类型,然后把这八位二进制数据一字节一字节的复制过去,所以常用的只有 0
、-1
、0x3f
,若是其他数据得到的结果和我们的预期会不一样。
值得一提的是 0x3f
被复制到 int
类型中得到的结果是 0x3f3f3f3f
,实际值为 1061109567
,即 10^9
级别。
另,memset
的时间复杂度和循环赋值是相同的 O(N)
级别,所以只使用循环赋值也没啥问题。
7.exit()
众所周知,在其他函数中使用 return
只能结束这次函数运行,没法结束程序运行。
但使用 exit(0)
可以结束程序运行,并返回 0
表示程序正常结束,在一些递归函数中使用会有奇效。
void func(int x) {
if (x == 1) {
exit(0);
}
cout << "函数调用前:" << x << "\n";
func(x-1);
cout << "函数调用后:" << x << "\n"; // 这段其实不会输出。
}
func(5);