【OI01-cpp】OI相关C++基础:命名空间、注释、输入与输出、#define
命令、变量、预处理命令、运算、读入/输出优化(部分)
MS Windows Error Codes
Windows 错误代码:[MS-ERREF]: Windows Error Codes
头文件#include
命名空间
申明空间
namespace A {
int cnt;
void f(int x) { cnt = x; }
} // namespace A
注:命名空间的声明是可以嵌套
namespace A {
namespace B {
void f() { ... }
} // namespace B
void f() {
B::f(); // 实际访问的是 A::B::f(),由于当前位于命名空间 A
// 内,所以可以省略前面的 A::
}
} // namespace A
void f() // 这里定义的是全局命名空间的 f 函数,与 A::f 和 A::B::f
// 都不会产生冲突
{
A::f();
A::B::f();
}
using
using
指令有如下两种形式:
using 命名空间::成员名;
:这条指令可以让我们省略某个成员名前的命名空间,直接通过成员名访问成员,相当于将这个成员导入了当前的作用域。
using namespace 命名空间;
:这条指令可以直接通过成员名访问命名空间中的 任何 成员,相当于将这个命名空间的所有成员导入了当前的作用域。
std标准命名空间
using namespace std
:引用std(所有)
using std::endl
:引用std中的endl
注释
输入与输出
cin与cout
cin>>
等价于cin.operator>>()
,同理cout
等价于cout.operator>>()
#include <iostream>
int main() {
int x, y; // 声明变量
std::cin >> x >> y; // 读入 x 和 y
std::cout << y << std::endl << x; // 输出 y,换行,再输出 x
return 0;
}
scanf与printf
大多数情况下,它们的速度比 cin 和 cout 更快,并且能够方便地控制输入输出格式。
#include <cstdio>
int main() {
int x, y;
scanf("%d%d", &x, &y); // 读入 x 和 y
printf("%d\n%d", y, x); // 输出 y,换行,再输出 x
return 0;
}
注:其中,%d
表示读入/输出的变量是一个有符号整型 (int 型)的变量。
类似地:
%s
表示字符串。%c
表示字符。%lf
表示双精度浮点数 (double)。%lld
表示长整型 (long long)。根据系统不同,也可能是%I64d
。%u
表示无符号整型 (unsigned int)。%llu
表示无符号长整型 (unsigned long long),也可能是%I64u
。
除了类型标识符以外,还有一些控制格式的方式。许多都不常用,选取两个常用的列举如下:
%1d
表示长度为1的整型。在读入时,即使没有空格也可以逐位读入数字。在输出时,若指定的长度大于数字的位数,就会在数字前用空格填充。若指定的长度小于数字的位数,就没有效果。%.6lf
用于输出,保留六位小数。
Why &
, \n
&
实际上是取址运算符,返回的是变量在内存中的地址\n
转义字符,表示换行- 搜索:cpp常见的转义字符
#define
命令
#define
是一种预处理命令,用于定义宏,本质上是文本替换。
#include <iostream>
#define n 233
// n 不是变量,而是编译器会将代码中所有 n 文本替换为 233,但是作为标识符一部分的
// n 的就不会被替换,如 fn 不会被替换成 f233,同样,字符串内的也不会被替换
int main() {
std::cout << n; // 输出 233
return 0;
}
宏可以带参数,带参数的宏可以像函数一样使用:
#include <iostream>
#define sum(x, y) ((x) + (y))
#define square(x) ((x) * (x))
using namespace std;
int main() {
cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << endl; // 输出 3 16
}
注:使用#define
是有风险的(由于#define
作用域是整个程序,因此可能导致文本被意外地替换,需要使用#undef
及时取消定义),因此应谨慎使用。较为推荐的做法是:使用const
限定符声明常量,使用函数代替宏。
但是,在 OI 中,#define 依然有用武之处(以下两种是不被推荐的用法,会降低代码的规范性):
#define int long long+signed main()
。通常用于避免忘记开 long long 导致的错误,或是调试时排除忘开 long long 导致错误的可能性。(也可能导致增大常数甚至 TLE,或者因为爆空间而 MLE)#define For(i, l, r) for (int i = (l); i <= (r); ++i)
、#define pb push_back
、#define mid ((l + r) / 2)
,用于减短代码长度。
与#ifdef
结合:
#ifdef LINUX
// code for linux
#else
// code for other OS
#endif
变量
变量名的合法性
数据类型
- 基础类型:
- void:无类型
- std::nullptr_t:空指针
- int:
符号性:signed/unsigned
大小:short、long、long long
扩展整数类型:GCC(__int128_t/__uint128_t) - bool:取值为true/false
- char:
符号性:signed/unsigned - float(单精度浮点类型)/double(双精度浮点类型)/long double(扩展精度浮点类型)
- 复合类型
- 定宽整数类型
注:
-
梯度:char,short,int,long,long long
1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
-
在不引发歧义的情况下,允许省略部分修饰关键字,或调整修饰关键字的顺序。这意味着同一类型会存在多种等价表述。
例如 int
,signed
,int signed
,signed int
表示同一类型,而 unsigned long
和 unsigned long int
表示同一类型。
- 浮点类型可以支持一些特殊值:
- 无穷(INFINITY)
- -0.0
- NaN
类型转换:
- 数值提升
- 整数提升
- 浮点提升
- 数值转换
定义变量
定义一个变量,需要包含类型说明符(指明变量的类型),以及要定义的变量名。
- 局部变量与全局变量(作用域不同):
int g = 20; // 定义全局变量
int main() {
int g = 10; // 定义局部变量
printf("%d\n", g); // 输出 g
return 0;
}
- 常量:
const int a = 2;
a = 3;
预处理命令
预处理命令就是预处理器所接受的命令,用于对代码进行初步的文本变换,比如 文件包含操作#include
和 处理宏#define
等,对 GCC 而言,默认不会保留预处理阶段的输出.i
文件。可以用-E
选项保留输出文件。
运算
自增/自减
i = 100;
op1 = i++; // op1 = 100,先 op1 = i,然后 i = i + 1
i = 100;
op2 = ++i; // op2 = 101,先 i = i + 1,然后赋值 op2
i = 100;
op3 = i--; // op3 = 100,先赋值 op3,然后 i = i - 1
i = 100;
op4 = --i; // op4 = 99,先 i = i - 1,然后赋值 op4
复合赋值 & 大小比较
与python相同:
-
复合赋值:
op = op + 2 可写为 op += 2,op = op - 2 可写为 op -= 2,op= op * 2 可写为 op *= 2
-
大小比较:与python相同
注:
- 赋值:
=
;等于号:==
if (op=1)
与if (op==1)
看起来类似,但实际功能却相差甚远。第一条语句是在对 op 进行赋值,若赋值为非 0 时为真值,表达式的条件始终是满足的,无法达到判断的作用;而第二条语句才是对 op 的值进行判断。
条件运算 & 逻辑运算
- 条件运算:
a ? b : c
中如果表达式 a 成立,那么这个条件表达式的结果是 b,否则条件表达式的结果是 c。 -
逻辑运算:
&&
:与||
:或!
:非Result = op1 && op2; // 当 op1 与 op2 都为真时则 Result 为真 Result = op1 || op2; // 当 op1 或 op2 其中一个为真时则 Result 为真 Result = !op1; // 当 op1 为假时则 Result 为真
读入、输出优化
std::ios::sync_with_stdio(false)
这个函数是一个「是否兼容 stdio」的开关,C++ 为了兼容 C,保证程序在使用了 printf 和 std::cout 的时候不发生混乱,将输出流绑到了一起。同步的输出流是线程安全的。这其实是 C++ 为了兼容而采取的保守措施,也是使 cin/cout 速度较慢的主要原因。
注:我们可以在进行 IO 操作之前将 stdio 解除绑定,但是在这样做之后要注意不能同时使用 std::cin 和 scanf,也不能同时使用 std::cout 和 printf,但是可以同时使用 std::cin 和 printf,也可以同时使用 scanf 和 std::cout。