调试技巧 与 编码注意事项
写程序检查
-
编译时,加上
-Wall,否则函数没有写return不会报错(windows c++14),inline函数没有指定返回值类型也不会报错(但是过不了linux的编译)。必要时,加上-Werror把 warning 变成 error 以避免眼瞎。 -
多测要清空!
-
如果怀疑是程序在某条分支出现错误,尝试在分支处输出不可能的答案,然后fc样例输出,看看这一答案对应的原样例输出和输入,说不定能发现什么猫腻。
-
我们离散化的时候不是会用到
unique吗?他的格式是这样的unique(start,end,compare),其中compare参数是代替==的比较函数,可选择不写。返回值是去重后的end。 -
调试过程: 分步调试,优先确定前面部分是否正确;输出中间值,善用
assert和cerr还有abort。如果是在找不出哪里错了,就重构可能出错的部分! -
不要忘记删除
assert! 否则会报错Runtime Error. Received signal 6: Aborted / IOT trap.。#include <cassert> #include <iostream> assert(bool,[char*]);//如果bool为false,输出char*(没有则不输出) cerr<<"Error"<<endl;//输出"Error"到标准错误流,不会干扰标准输出流 abort c;//抛出错误c,c可以是数字、字符串等任意类型(? -
精度问题: 如果数据很大远超
long long而且题目没让你取模,那么大概率是输出浮点数。这时要注意所有可能爆long long的地方都要用浮点数(double)。另外,eps最好设置小一点(至少比要求的精度小 \(2\) 个数量级)。 -
输出精度问题:
double输出时不一定会把所有位数都输全,所以要写setprecision才能保证输出的精度正确。 -
数据类型存储范围: 记住
int范围在 \(2\times 10^9\) 左右,long long范围在 \(9\times 10^{18}\) 左右,unsigned是两倍范围。切记,在 \(10^9\) 的要乘法要开long long,在 \(10^{18}\) 的要乘法的要开__int128。记住long long下溢出但没有溢出ull的转化为ull后会变为正确的数字。大部分二元运算会把两侧的强转为较高范围的一级。 -
输出变量名: 如果你想输出变量但又分不清变量是哪个,可能会想要输出变量名。一个一个地敲实在是太慢了,试试这个!
#define PT(name) cout<<(#name)<<"="<<name<<" ";此处
(#name)会把替代name的事物的名称直接转换为字符串输出,因此这个“函数”可以输出a=1这样的效果。 -
除以2问题:
/2是向0取整,>>1是向下取整 -
斜率优化之类的代码量小的题目还是别对拍了。对拍也看不出答案,不如瞪眼法。
编译器问题
- DEV-C++ 编译器可能会允许没有指定返回值类型的函数通过编译(我因为这个问题写挂了两次BIT),但是某些平台如 HydroOJ 不行。
- 注意保留 X 位小数的要求!编译器可能默认输出 \(4\) 位,但是别的编译器未必如此。
卡常
memset在赋值 \(0\) 或者 \(-1\) 的时候非常快,而赋值 0x3f 的时候就比较慢了。务必注意数组很大的时候memset常数带来的区别。
一些卡常的技巧:
- 函数返回类型前加
inline(据说O2会自动优化) - 修改枚举顺序,或者修改数组两个维度的顺序,利用内存连续访问的特性(例如矩阵乘法,或者把st表log维放在前面)
- 非必要不开long long。特别是大量取模的情况。
- 循环变量类型前加
register - 函数传入的参数如果不需要修改,那么写成
const int &a - 慎用
memset,特别是多测 - 慎用 vector,特别是扫描线和建图。有数倍常数。
- 关闭输入输出同步流:
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
- 注意此时不能用
endl,而是要用"\n",否则刷新缓冲区会耗费很多时间。特别适用于大量换行的题目。
使用 GPROF
编译 g++ -pg -o test test.cpp
运行程序之后会生成 gprof.out
最后 gprof test.exe gmon.out 就会输出每个函数消耗的时间。
提交前检查
空间开没开够,开没开多,会不会MLE?
跑一遍手生成的大样例看看会不会TLE?
检查有没有编译器的逆天错误,例如非void函数没有return 或者 函数没有声明返回值类型。
检查freopen。
提交后检查
如果在本地和OJ评测结果不一样,可能是因为:
- 有未定义行为:加入
-Wall -fsanitize=leak,undefined,address编译选项;检查函数有没有返回值,有没有地址溢出,有没有变量未初始化 - 栈空间不足:本地栈空间不足,可以使用
-Wl,--stack=536870912命令开出 \(512\) MB空间(后面的数字是字节数)。 - 评测机环境与本地不同:尤其注意windows环境下的换行字符有
\r,而linux环境下没有。 - 对于
\r:如果使用getline等等输入方式读入一整行,会读入\r作为字符串的一部分,此时需要特判并删除。
基于 IOI 赛制的方法:在可能错误的地方加入assert或者abort,如果其他错误变成了 \(\color{purple}\text{RE}\) 说明你猜对了。
洛谷评测机不会递归爆栈,但是即使是数组越界也可能WA(特别是数组较小的时候)!
queue、deque 的 MLE:为了性能,queue 在 pop 时不会释放内存,所以把多个 queue 反复倒来倒去可能会导致 MLE。另外,建立一个queue初始就会自带约10倍内存常数,造成巨大负担。避免使用queue、deque!
Signal 7: Bus Error :总线错误。访问了非法的地址(如很小的负数下标),并开启 O2 优化后可能会出现,不过根据数组大小,可能也有 Signal 11: Segmentational Fault 出现。

浙公网安备 33010602011771号