C++基础及实用技巧

· · 算法·理论

本章主要讲述 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{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} 的时候需要记得把取址运算符加上。

转义字符

转义字符用来表示一些无法直接输入的字符,如由于字符串字面量中无法换行而无法直接输入的换行符,由于有特殊含义而无法输入的引号,由于表示转义字符而无法输入的反斜杠。

  1. \t 表示制表符。
  2. \\ 表示 \
  3. \" 表示 "
  4. \0 表示空字符,用来表示 C 风格字符串的结尾。
  5. \r 表示回车。\text{Linux} 中换行符为 \n\text{Windows} 中换行符为 \r\n。在 \text{OI} 中,如果输出需要换行,使用 \n 即可。但读入时,如果使用逐字符读入,可能会由于换行符造成一些问题,需要注意。例如,\text{gets}\n 作为字符串结尾,这时候如果换行符是 \r\n\r 就会留在字符串结尾。
  6. 特殊地,%% 表示 %,只能用在 \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); ```