From 9875ddbdcc37e67b0a1a88891f56914fd6fa32af Mon Sep 17 00:00:00 2001 From: Trisolaris HD <36555123+TrisolarisHD@users.noreply.github.com> Date: Wed, 27 Mar 2019 16:15:38 +0800 Subject: [PATCH] Update common-mistakes.md MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 更改了该页面的排版。 --- docs/intro/common-mistakes.md | 103 ++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 40 deletions(-) diff --git a/docs/intro/common-mistakes.md b/docs/intro/common-mistakes.md index f5671f5c..44c37bdb 100644 --- a/docs/intro/common-mistakes.md +++ b/docs/intro/common-mistakes.md @@ -1,43 +1,30 @@ 本页面主要分享一下在竞赛中经常/很多人会出现的错误。 -- 由于运算符优先级产生的错误。 - - `1 << 1+1` : 1 左移了 2,即该表达式返回的值是 `4` 。 - - 由于宏的展开,且未加括号导致的错误: - ```cpp - #define pwr(x) x* x - pwr(2 + 2) - ``` - 该宏返回的值并非 $4^2 = 16$ 而是 $2+2\times 2+2 = 8$ 。 +## 会引起 Compile Error 的错误 -- 文件操作有可能会发生的错误。 +由于这类错误过于简单,~~相信是个正常人都会修~~,故略写. - - 对拍时未清除文件指针即 `fclose(fp)` 就又令 `fp = fopen()` , 这会使得进程出现大量的文件野指针。 - - `freopen()` 中的文件名未加 `.in` / `.out` 。 +- `int main()` 写为 `int mian()` 。 -- `int mian()` 。 +- 写完 `struct` 或 `class` 忘记写分号。 -- 无向图边表未开 2 倍。 -- 线段树未开 4 倍空间。 +## 不会引起 Compile Error 但会引发 Warning 的错误 -- 多组数据未清空数组。 +这类错误较难发现,但会在使用 `-W{warningtype}` 参数编译时被编译器指出,所以要多学会使用 `-W{warningtype}` 参数,常见的有 `-Wall`,`-Wextra`,`-Wshadow` 等。 -- 分治未判边界导致死递归。 +- 由于运算符优先级产生的错误。 + - `1 << 1 + 1` : 1 左移了 2,即该表达式返回的值是 `4` 。 -- 读入优化未判断负数。 +- 文件操作有可能会发生的错误。 + + - 对拍时未清除文件指针即 `fclose(fp)` 就又令 `fp = fopen()` , 这会使得进程出现大量的文件野指针。 + - `freopen()` 中的文件名未加 `.in` / `.out` 。 - 不正确地使用 `static` 修饰符。 - `-1 >> 1 == 1` 。 -- 写完 `struct` 或 `class` 忘记写分号。 - -- 存图下标从 0 开始输入节点未 -1。 - -- BFS 时不标记某个状态是否已访问过。 - -- 大/小于号打错或打反。 - - 赋值运算符和 `==` 不分。 - 示例: ```cpp @@ -45,28 +32,64 @@ else puts("No"); ``` 无论 $n$ 的值之前为多少,输出肯定是 `Yes`。 - -- 没有考虑数组下标出现负数的情况 +- 使用 `scanf` 读入的时候没加取地址符 `&``。 + +- 没有考虑数组下标出现负数的情况。 + -- scanf 读入的时候没加 & 取地址符 +## 既不会引起 Compile Error 也不会引发 Warning 的错误 -- 在执行 `ios::sync_with_stdio(false);` 后混用两种 IO,导致输出错乱。 - - 可以参考这个例子。 +这类错误无法被编译器发现,所以在调试时只能依靠你自己. + +- 无向图边表未开 2 倍。 + +- 线段树未开 4 倍空间。 + +- 多组数据未清空数组。 + +- 分治未判边界导致死递归。 + +- 读入优化未判断负数。 + +- 存图下标从 0 开始输入节点未 -1。 + +- BFS 时不标记某个状态是否已访问过。 + +- 大/小于号打错或打反。 + +- 在执行 `ios::sync_with_stdio(false);` 后混用两种 IO,导致输入/输出错乱。 + - 可以参考这个例子。 ```cpp //这个例子将说明,关闭与 stdio 的同步后,混用两种 IO 的后果 //建议单步运行来观察效果 - #include #include - using namespace std; + #include int main() { - ios::sync_with_stdio(false); - //关闭IO后,cin/cout将使用独立缓冲区,而不是将输出同步至scanf/printf的缓冲区,从而减少IO耗时 - cout<<"a\n"; - //cout下,使用'\n'换行时,内容会被缓冲而不会被立刻输出,应该使用endl来换行并立刻刷新缓冲区 - printf("b\n"); - //printf的'\n'会刷新printf的缓冲区,导致输出错位 - cout<<"c\n"; - return 0;//程序结束时,cout的缓冲区才会被输出 + std::ios::sync_with_stdio(false); + //关闭同步后,cin/cout 将使用独立缓冲区,而不是将输出同步至 scanf/printf 的缓冲区,从而减少 IO 耗时 + std::cout << "a\n"; + //cout 下,使用'\n'换行时,内容会被缓冲而不会被立刻输出,应该使用 endl 来换行并立刻刷新缓冲区 + printf("b\n"); + //printf 的 '\n' 会刷新 printf 的缓冲区,导致输出错位 + std::cout << "c\n"; + return 0;//程序结束时,cout 的缓冲区才会被输出 } ``` + +- 由于宏的展开,且未加括号导致的错误: + ```cpp + #define square(x) x * x + printf("%d", square(2 + 2)); + ``` + 该宏返回的值并非 $4^2 = 16$ 而是 $2+2\times 2+2 = 8$ 。 + +- 使用宏展开编写 min/max + 这种做法虽然算不上是「错误」,但是这里还是要拎出来说一下。 + 常见的写法是这样的: + ```cpp + #define Min(x, y) (x < y ? x : y) + #define Max(x, y) (x > y ? x : y) + ``` + 这样写虽然在正确性上没有问题,但是如果你直接对函数的返回值取 max,如 `a = Max(func1(), func2())`,而这个函数的运行时间较长,则会大大影响程序的性能,因为宏展开后是 `a = func1() > func2() ? func1() : func2()` 的形式,调用了三次函数,比正常的 max 函数多调用了一次。 + 这种错误在初学者写线段树时尤为多见。 -- 2.11.0