debug检查单

当你的程序没有AC时,请按此检查单一项一项检查。

首先看这个

  1. 电脑坏了,键盘鼠标坏了,IDE坏了,账号上不去了,电脑没电了,运行不了代码了,题目/数据有问题……

    重启IDE,再不行就重启电脑,还不行就举手向监考老师提问。

  2. 做题时一字不落地阅读整个题目(包括提示、Hint)了吗?

  3. 编译时打开「显示所有警告信息(-Wall)」开关了吗?

  4. 能看懂编译器给出的警告吗?妥善处理了这些警告吗?

    scanfprintf 函数里用 %lld 输入输出 long long 类型变量时,会产生这样的警告:

    1
    2
    [警告] unknown conversion type character 'l' in format [-Wformat=]
    [警告] too many arguments for format [-Wformat-extra-args]

    这是可以忽略的。其它的警告,一般都不能忽略,需要消除。

  5. 然后,请根据你的具体错误选择对应的检查单

    怎么看?

    比赛页面左侧,点击「我的提交」:

    image-20231106224033830

    把鼠标放到「结果」那一栏上:

    image-20231106224123162

CE(Compile Error)

你的程序存在语法错误(C / C++ 最常见的是缺少分号、缺少括号、使用了中文标点符号或者函数调用错误等等)或者OJ系统不支持的写法(较少见)。

此时应当仔细检查代码在本机能否通过编译,改正后再次提交。

  1. 保存文件时,后缀名是 .c 而不是 .cpp 吗?

  2. 编译器的标准选到 ISOC99 了吗?

  3. 能看懂编译器给出的报错信息吗?用翻译软件翻译一下呢?

  4. 输出特殊字符时,合理地进行转义了吗?

  5. 是否使用了不在标准库中的函数?

    例如:strrev

报错:[错误] ld returned 1 exit status

  1. 使用的各库函数名、关键字拼写正确吗?

    常见易错:mainprintfscanfsinvoid

  2. 之前运行程序打开的控制台(黑框框)关掉了吗?

  3. 实在不行,新建一个 .c 文件,并把你原来的代码粘贴进去,再编译试试。

报错:[错误] stray '\xxx' in program

  1. 程序里有全角(中文)符号吗?

    半角符号(正确):

    1
    . ; : ' " ! ( ) [ ]

    全角符号(错误):

    1
    。 ; : ’ “ ! () 【】

WA(Wrong Answer)

你程序输出的结果有错误,与期望输出不匹配(也有可能是因为缺少了必要的换行和空格)。

请检查你的程序是否出现了致命的逻辑错误,当然有的时候是因为手滑。

  1. 用到的运算有可能产生数据溢出吗?数据溢出的情况得到妥善处理了吗?

    例如:\(a\)\(b\) 都是 int 类型的变量,那么 \(a+b\)\(a-b\)\(a\times b\) 都有可能超过 int 型上下限,从而产生数据溢出。即便你使用一个 long long 类型的变量承接表达式的值,可是表达式计算过程中已经发生了溢出,仍然会导致结果错误。

    请对照下面的代码,思考错误的原因和正确的处理方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int a,b;
    a=2147483647;
    b=2;
    long long c,d;
    c=a+b;
    d=a*b;
    printf("%lld %lld\n",c,d);
    //错误,输出-2147483647 -2
    c=(long long)a+b;
    d=(long long)a*b;
    printf("%lld %lld\n",c,d);
    //正确,输出2147483649 4294967294

    再例:有些情况下,即便题目保证了输入数据和结果都在 int 范围内,但是中间计算过程仍然会发生溢出。

    1
    2
    3
    4
    5
    6
    7
    int a,b,c;
    a=2000000000;
    b=2000000000;
    c=5000;
    int d=(a+b)/c;
    //在本代码中,abc,以及d本应得到的值都在int范围内
    //但a+b已经超过了int范围,因此会得到错误的结果。
  2. 如果程序用到了浮点数,那么合理地处理了浮点误差和 NaN 吗?

    例如:不能用 == 判断两个浮点数是否相等,计算整数的整数次方时不要用 pow 函数,浮点数除法的除数、\(\log (x)\) 里的 \(x\) 是否可能是 \(0\)

  3. 希望其值为浮点数的算式,值真的是浮点数吗?

    请对照下面的代码,思考错误的原因和正确的处理方式。

    1
    2
    3
    4
    double a=1/3;
    printf("%f",a);//a=0
    a=1.0/3.0;
    printf("%f",a);//a=0.3333...
  4. 程序的输入部分输入的内容真的是正确的吗?

    在输入结束以后,把输入的变量输出一下看看。

  5. 输出特殊字符时,合理地进行转义了吗?

  6. 重复声明了相同名称的变量吗?

    错误示例1:

    1
    2
    3
    4
    int i;
    for(int i=1;i<=n;++i){
    //i在不同作用域中被重复声明
    }

    错误示例2:

    1
    2
    3
    4
    5
    6
    7
    8
    int x;
    long long myPow(int a, int x) {//函数形参和全局变量重复
    long long ans = 1LL;
    for (int i = 1; i <= x; ++i) {
    ans *= a;
    }
    return ans;
    }
  7. 使用 printf 语句输出时,类型标识符和输出的变量类型的对应关系是正确的吗?

    int 改成 long long 的时候,输出的 %d 改成 %lld 了没?

  8. 输出给定的字符串时,是从题目上复制粘贴的吗?

  9. 特殊情况、边界情况考虑了吗?

    例如:区间合并,输入只有一个区间;进制转换,被转换的数为零……

  10. 在声明任何变量、数组、指针、字符串时,给它赋予了合理的初始值吗?

    除非你这个变量(数组、指针、字符串……)是声明了以后立刻输入的,否则必须赋予初始值,不管是全局还是局部。

  11. 多组输入输出问题中,在每组开始时,给用到的变量、数组清零或重新初始化了吗?

    其实不只是每组开始时,最好能检查一下整个程序里该初始化的地方都初始化了没。

  12. 多组输入输出问题中,在输出每组答案以后换行了吗?

REG(Runtime Error)

这种错误分很多种,具体请百度。最常见的是 SIGSEGV,一般是因为访问了不存在的数组元素或者函数调用过多导致爆栈。

在本地,这样的错误往往表现为输入了数据以后,程序卡死,过一会儿后结束,然后程序的返回值不是零,例如:

image-20231106224607702

... with return value 后面的那个 3221225620 就是程序的返回值。当它不是 0 时,说明你的程序发生了运行时错误。

  1. 数组的长度开得够大吗?

    如果你习惯下标从 \(1\) 开始,那么数组的长度要加一。

    字符串注意 \0 也要占一位。

    \(10^x\) 就是 \(1\) 后面有 \(x\)\(0\)

    字符串做拼接操作时,是否预留了足够的空间?

  2. 用变量做数组的长度了吗?

    这种写法是错误的,请思考为什么:

    1
    2
    3
    4
    5
    6
    int n;
    int a[n];
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
    scanf("%d",&a[i]);
    }
  3. 使用 scanf 语句时,加 & 符号了吗?

    字符串不用加,别的都得加。

  4. 使用除法 / 或取模 % 运算时,除数或模数有可能是零吗?

    有可能因为数据溢出而导致意外的 \(0\) 。例如:

    image-20231124162927558
  5. 递归调用函数的层数是否太多了?能改写成循环吗?

    例如:计算阶乘、二分法等,都要写成循环

  6. 在使用下标访问数组元素时,下标会小于 \(0\) 或超过数组长度吗?

  7. 长度超过十万(\(10^5=100000\))的数组,开成全局的了吗?

  8. 主函数结束的时候写 return 0 了吗?

TLE(Time Limit Exceeded)

你的程序可能因为时间效率不高或者出现了死循环,所以未能在规定的时限内运行结束

在本地,这样的错误往往表现为输入数据以后程序没有反应,也不退出。

  1. 程序是否有死循环?

    检查方法:

    1. 在每个模块前加入调试输出语句,例如:在输入完成后加一行

      1
      printf("输入完成!\n");
    2. 运行程序,找到程序在哪个循环处死循环了

    3. 在出现死循环的循环中,输出循环控制变量,进行详细检查。

  2. 多组输入输出问题时,是否设置了正确的终止条件?

    scanf(...)!=EOFgets(...)!=NULL

  3. 递归调用函数的效率是否太低了?能改写成循环吗?

    例如:计算斐波那契数列

  4. 评测机一秒只能运算 \(10^8\) 次,你循环的次数是否太多了?

    OJ上题目的时间限制大部分是\(1000\text{ms}\),而\(1000\text{ms}\)评测机能够计算的次数大概不超过1亿次。所以当你的代码中 for 循环,或 while 循环循环了超过一亿次时,请果断更换解题方法。

MLE(Memory Limit Exceeded)

你的程序占用的内存超过了规定值,可能是因为使用了过大的数组,也可能是没有做到内存释放(较少见)。

  1. 请计算程序所占用的内存,检查是否超过了限制。

    一般来说,一道题的内存限制是 \(65536 \text{KB}\),即 \(65536\) 千字节。

    一个 int 占用 \(4\) 个字节,与此同时,一个空的程序还要占用一定的基础内存。也就是说,你最多只能开长度为 \((65536-1200)\times 1024/4 =16470016\)int 数组。

    保险起见,最多开 \(10000000=10^7\) 长度的 int 数组。

PE(Presentation Error)

你的程序几乎能AC了,但是和标准输出数据有点细微的差距(大小写,空格数量,换行数量之类的)。

此时应当仔细观察题目给出的输出样例,确认格式无误(选中数据粘贴到编辑器最为稳妥)。

  1. 认真阅读题目的「输出格式」部分了吗?
  2. 多组输入输出问题中,在输出每组答案以后换行了吗?

OE(Other Error)

其他错误,详询老师或者助教。

  1. 把它先当REG(Runtime Error)看。

作者:梁秋月、逐月的游星


debug检查单
https://suzumiyaakizuki.github.io/2023/11/17/debug 检查单/
作者
SuzumiyaAkizuki
发布于
2023年11月17日
许可协议