C++基础及实用技巧
zifuneko
·
·
算法·理论
本章主要讲述 C++ 中 \text{inline}、\text{printf}、\text{scanf}、\text{define}、\text{typedef} 的用法及其一些基础或技巧。
\text{inline}
在 \text{C/C++} 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 \text{inline} 修饰符,表示为内联函数。
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
**以下情况不宜使用内联:**
- 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
- 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。要当心构造函数和析构函数可能会隐藏一些行为。所以不要随便地将构造函数和析构函数的定义体放在类声明中。
# $\text{main()}
\text{主函数 main()}
什么是 \text{main()} ?
可以理解为程序运行时就会执行 \text{main()} 中的代码。
实际上,\text{main()} 函数是由系统或外部程序调用的。如,你在命令行中调用了你的程序,也就是调用了你程序中的 \text{main()} 函数(在此之前先完成了全局 变量 的构造)。
最后的 \text{return 0} ; 表示程序运行成功。默认情况下,程序结束时返回 0 表示一切正常,否则返回值表示错误代码(在 \text{Windows} 下这个错误代码的十六进制可以通过 \text{Windows Error Codes} 网站 进行查询)。这个值返回给谁呢?其实就是调用你写的程序的系统或外部程序,它会在你的程序结束时接收到这个返回值。如果不写 \text{return} 语句的话,程序正常结束默认返回值也是 0。
在 \text{C} 或 \text{C}++ 中,程序的返回值不为 0 会导致运行时错误(\text{RE})。
\text{注释}
在 C++ 代码中,注释有两种写法:
注释对程序运行没有影响,可以用来解释程序的意思,还可以在让某段代码不执行(但是依然保留在源文件里)。
在工程开发中,注释可以便于日后维护、他人阅读。
进行多行注释:
-
在 \text{CodeBlocks} 中,使用 \text{Ctrl+Shift+C} 快捷键可以进行多行注释,使用 \text{Ctrl+Shift+X} 快捷键可以取消多行注释。上方 \text{Edit}(编辑) 中的 \text{Comment}(注释) 以及 \text{Uncomment}(取消注释) 同样可以快速进行多行注释。
-
在 \text{DevC} 中,使用 \text{Ctrl+/} 快捷键可以进行多行注释,再次使用后可以取消多行注释。
\text{CodeBlocks} 的更多用法:
在 \text{CodeBlocks} 中,使用 \text{Ctrl+R} (\text{Replace}) 快捷键可以进行快速替换。使用 \text{Ctrl+F} (\text{Find}) 快捷键可以进行快速查找。\text{DevC} 同理。
\text{printf \& scanf}
大多数情况下,它们的速度比 \text{cin} 和 \text{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;
}
我们在使用 \text{printf} 的时候可以直接在其中输出字符串,例如 printf("114514");。
特别的,在使用 \text{scanf} 的时候需要记得把取址运算符加上。
-
为什么 \text{scanf} 中有 \& 运算符?
在这里,\& 实际上是取址运算符,返回的是变量在内存中的地址。而 \text{scanf} 接收的参数就是变量的地址。
-
下方是一些常见的类型:
-
-
-
-
-
-
-
-
除了类型标识符以外,还有一些控制格式的方式。许多都不常用,选取两个常用的列举如下:
-
-
转义字符
转义字符用来表示一些无法直接输入的字符,如由于字符串字面量中无法换行而无法直接输入的换行符,由于有特殊含义而无法输入的引号,由于表示转义字符而无法输入的反斜杠。
\t 表示制表符。
\\ 表示 \。
\" 表示 "。
\0 表示空字符,用来表示 C 风格字符串的结尾。
\r 表示回车。\text{Linux} 中换行符为 \n,\text{Windows} 中换行符为 \r\n。在 \text{OI} 中,如果输出需要换行,使用 \n 即可。但读入时,如果使用逐字符读入,可能会由于换行符造成一些问题,需要注意。例如,\text{gets} 将 \n 作为字符串结尾,这时候如果换行符是 \r\n,\r 就会留在字符串结尾。
- 特殊地,
%% 表示 %,只能用在 \text{printf} 或 \text{scanf} 中,在其他字符串字面量中只需要简单使用 % 就好了。
\text{define}
```cpp
#include <iostream>
#define n 233
// n 不是变量,而是编译器会将代码中所有 n 文本替换为 233,但是作为标识符一部分的
// n 的就不会被替换,如 fn 不会被替换成 f233,同样,字符串内的也不会被替换
int main()
{
std::cout << n; // 输出 233
return 0;
}
```
宏可以带参数,带参数的宏可以像函数一样使用:
```cpp
#include <iostream>
#define sum(x, y) ((x) + (y))
using namespace std;
int main()
{
// 输出 3 16
cout << sum(1, 2) << ' ' << 2 * sum(3, 5) << endl;
return 0;
}
```
使用 $\text{\#define}$ 是有风险的(由于 $\text{\#define}$ 作用域是整个程序,因此可能导致文本被意外地替换,需要使用 $\text{\#undef}$ 及时取消定义),因此应谨慎使用。较为推荐的做法是:使用 $\text{const}$ 限定符声明常量,使用函数代替宏。
但是,在 $\text{OI}$ 中,$\text{\#define}$ 依然有用武之处(下方是不被推荐的用法,会降低代码的**规范性**):
$\text{\#define int long long}$ + $\text{signed main()}$。通常用于避免忘记开 $\text{long long}$ 导致的错误,或是调试时排除忘开 $\text{long long}$ 导致错误的可能性。(也可能导致增大常数甚至 $\text{TLE}$,或者因为爆空间而 $\text{MLE}$)
如下:
```cpp
#include <iostream>
#define int long long
using namespace std;
signed main()
{
return 0;
}
```
# $\text{typedef}
如下:
```cpp
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
int main()
{
ULL a=1e64-1;
cout << a << endl;
return 0;
}
```
# $\text{fixed}\ \&\ \text{setprecision}
如果不使用任何特定的流操纵符来控制输出,C++ 程序中的浮点数输出将遵循默认的规则和行为。如果数值非常大或非常小,以科学计数法输出。保留有效位至多 $6$ 位(有效位包括小数点前的位数)。
## $\text{fixed}
```cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double num1 = 123456789.123;
double num2 = 123456789.123456;
double num3 = 123456789.1234567;
cout << fixed << num1 << endl; //输出为 123456789.123000
cout << fixed << num2 << endl; //输出为 123456789.123456
cout << fixed << num3 << endl; //输出为 123456789.123457
return 0;
}
```
## $\text{setprecision}
单独使用 \text{setprecision(n)} 会设置输出流的精度为 n 位有效数字,这意味着输出的数字将尽可能保留 n 位非零数字。
组合使用 \text{fixed} 和 \text{setprecision}:
当这两个操纵符一起使用时,可以精确控制浮点数的输出格式。\text{fixed} 确保数字以固定小数点格式输出,而 \text{setprecision} 指定小数点后的位数(不是有效位位数)。
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double num = 123456789.123456789;
cout << fixed << setprecision(2) << num << endl; // 输出为 123456789.12
return 0;
}
加快 \text{cin}、\text{cout} 运行效率的方法
\text{ios::sync\_with\_stdio(false)}
在调用 \text{ios::sync\_with\_stdio(false)} 后,\text{cout} 与 \text{stdout} 不再共享同一块缓冲区,它们分别管理自己的缓冲区。简述,函数作用为设置标准 \text{C++} 流是否与标准 \text{C} 流在每次输入或输出操作后同步
正是因为这种同步,所以 \text{cin}、\text{cout} 比 \text{scanf}、\text{printf} 速度要慢,如果我们在使用 \text{cin}、\text{cout} 输入输出前加一句 \text{ios::sync\_with\_stdio(false)} ,即取消缓冲区同步,可节省时间,效率与 \text{scanf}、\text{printf} 相差无几。
\text{tie}
$\text{cin}$ 默认是与 $\text{cout}$ 绑定,所以每次 $\text{cin}$ 操作的时候都在联系流(即输出流)调用 $\text{flush()}$,这样增加了 $\text{IO}$ 负担,通过 `cin.tie(nullptr)` 来解除 $\text{cin}$ 和 $\text{cout}$ 之间的绑定,进一步加快执行效率。
代码如下:
```cpp
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
```