常见的错误和警告
参考了来自 OI WIKI 常见错误和 Orz panda 常见错误等。 有时间后面再更。
编译错误的可能情况¶
- 选择了不恰当的语言或编译器进行编译。
- 在 XDOJ 中, 推荐使用 GNU C/C++, 进行编译。
- 拼写错误,
int main()写为int mian()。 - 写完
struct或class忘记写分号。 - 数组开太大, (在 OJ 上)使用了不合法的函数(例如多线程), 或者函数声明但未定义, 会引起链接错误。
- 函数参数类型不匹配。
- 如使用
<algorithm>头文件中的max函数时, 传入了一个int类型参数和一个long long类型参数。
- 如使用
- 使用
goto和switch-case的时候跳过了一些局部变量的初始化。 - 滥用
<bits/stdc++.h>。
编译警告的可能情况¶
这类错误时写下的程序虽然能通过编译, 但大概率会得到错误的程序运行结果。这类错误会在使用 -W{warningtype}(比如说 -Wall ) 参数编译时被编译器指出。
-
赋值运算符
=和比较运算符==不分。- 如果确实想在原应使用
==的语句里使用=(比如while (foo = bar)), 又不想收到 Warning, 可以使用 双括号:while ((foo = bar))。
- 如果确实想在原应使用
-
由于运算符优先级产生的错误。如果你不知道优先级, 加个括号总是没问题的。
- 不正确地使用
static修饰符。 - 使用
scanf读入的时候没加取地址符&。 - 使用
scanf或printf的时候参数类型与格式指定符不符。- 注意运算中可能带来的参数类型转换。
- 同时使用位运算和逻辑运算符
==并且未加括号。- 示例:
(x >> j) & 3 == 2
- 示例:
- int 字面量溢出。
- 示例:
long long x = 0x7f7f7f7f7f7f7f7f, 1<<62。
- 示例:
-
带符号整数的溢出。
- 注意数据范围, 考虑使用
long long,但不要在任何情况下直接使用long long, 可能会导致常数偏大或爆空间。 - 带符号整数的溢出是未定义行为, 编译器输出的值 可能为任意值 , 参照CWE-190: Integer Overflow or Wraparound。
- 使用
-fsanitize=undefined -g即可使得你的程序在触发带符号溢出时, 输出错误消息并显示对应的代码行号。但是你仍然需要先构造一组测试用例, 使得计算时使用的值大到可以产生溢出。 - 由于编译器的优化, 可能会导致
- 注意数据范围, 考虑使用
-
误加了
;, 例如while(a>0);。 -
未初始化局部变量/数组。
未初始化变量会发生什么
原文:https://loj.ac/d/3679 by @hly1204
例如我们在 C++ 中声明一个 int a; 但不初始化, 可能有时候会认为 a 是一个「随机」(其实可能不是真的随机)的值, 但是可能将其认为是一个固定的值, 但实际上并非如此。
我们在简单的测试代码中
https://wandbox.org/permlink/T2uiVe4n9Hg4EyWT
代码是:
#include <iostream>
int main() {
int a;
std::cout << std::boolalpha << (a < 0 || a == 0 || a > 0);
return 0;
}
在一些编译器和环境上开启优化后, 其输出为 false。
有兴趣的话可以看 https://www.ralfj.de/blog/2019/07/14/uninit.html, 尽管其是用 Rust 做的实验, 但是本质是一样的。
可能会导致答案错误的情况¶
- 题交错了。
- 题读假了。
- 不测样例就交, 结果没过样例。
- 只测样例就交, 对自己炒鸡自信。
- 变量重名或混用。
- 循环变量的混用。scd
- 和输入输出格式中的声明不同, 理解不清导致混用。
- 和标准库中函数或变量的重名。
- 上一组数据处理完毕, 读入下一组数据前, 未清空数组。
- 需要注意的是并不是所有的题都需要进行数组清空, 可能导致复杂度过大。
- 读入优化未判断负数。
-
所用数据类型位宽不足, 导致溢出。
- 如习语「三年 OI 一场空, 不开 long long 见祖宗」所描述的场景。选手因为没有在正确的地方开 long long(将整数定义为 long long 类型), 导致得出错误的答案而失分。
-
存图时, 节点编号 0 开始, 而题目给的边中两个端点的编号从 1 开始, 读入的时候忘记 -1。
- 大/小于号打错或打反。
-
在执行
ios::sync_with_stdio(false);后混用scanf/printf和std::cin/std::cout两种 IO, 导致输入/输出错乱。 -
在交互题内使用
cin.tie()且不使用endl和flush刷新缓冲区。- 不使用
cout.tie(), 该语句没有任何作用。
- 不使用
-
由于宏的展开, 且未加括号导致的错误。
- 没有删除或注释掉调试输出语句。
- 哨兵值设置错误。例如, 平衡树的 0 节点。
- 在类或结构体的构造函数中使用 : 初始化变量时, 变量声明顺序不符合初始化时候的依赖关系。
- 并查集合并集合时没有把两个元素的祖先合并。
未定义行为¶
未定义行为一般会导致未知的结果。
- 除以 0
-
数组下标越界
-
未正确设置循环的初值导致访问了下标为 -1 的值。
-
无向图边表未开 2 倍。
-
线段树未开 4 倍空间。
-
看错数据范围, 少打一个零。
-
错误预估了算法的空间复杂度。
-
写线段树的时候, pushup 或 pushdown 叶节点。
-
-
除 main 外有返回值函数执行至结尾未执行任何 return 语句
- 即使有一个分支有返回值, 但是其他分支却没有, 结果也是未定义的。可以向编译选项中追加 -Wall, 检查编译器是否给出有关于函数未 return 的警告。
可能导致运行时错误¶
- 没有删除或注释文件操作语句(在某些OJ上)。
- 在 DOMJUDGE 中, MLE 标记为 RE 。
可能导致超时¶
- 算法复杂度不对, 或出题人故意卡掉了你的算法。
- 分治未判边界导致死递归。
- BFS 时不标记某个状态是否已访问过。
- 使用宏来展开编写
max或min。
可能导致超内存¶
- 数组过大。
- STL 容器中插入了过多的元素。