课程 · C++ 速查 · 报错了怎么读

报错了怎么读 例题与练习

这一组的题形状不一样:先给你一版错的,连它真实的报错一起,请你说出它错在哪、再改好。⚠ 下面每一段报错都是真的跑出来抄的,不是编的。

2 道例题3 道练习5 份可运行代码回速查看这一组的 7 条 →
怎么用这一页

例题给全套:题面、样例、参考代码、以及一步步的讲解。先自己读题想一遍, 再看代码,最后对着讲解核一遍自己想的对不对。

练习只给题面、样例和一句提示,答案是折叠起来的 —— 请先自己写一版跑通了再展开。写不出来也别直接看答案,先回速查那一组找找。

每份代码都能直接点运行:输入框里已经填好了样例输入, 把它改一改再跑,是这一页最值钱的用法。

先把地基摆一遍

3 段

★ 先记一条通用的读法:只看第一条错误,改完再编译一遍。C++ 的编译错误会「雪崩」—— 前面一个分号引发后面几十条,而后面那几十条全是噪声。一次只改最上面那一条。

★★ 第二条:报错的行号常常指到出错行的下一行。 因为编译器是读到下一行才发现「上一句没写完」。看到行号先往上看一行。

⚠ 第三条,也是最要紧的:能编译过不等于是对的。 越界、溢出、没初始化、递归太深 —— 这四样编译器一句话都不会说,它们要么在运行时崩,要么安静地给出一个错答案。下面最后三条讲的就是它们。

例题(2 道)

每道都给全套:题面 → 样例 → 参考代码 → 一步步的讲解。

例题 ① 漏了分号

errEx1.cpp

下面这段程序编译不过。请读懂报错,改好它。

改好之后它该做的事是:第一行读一个整数 n,接下来 n 行每行两个整数 xy,对每一行输出 x + y

输入格式第一行一个整数 n1 ≤ n ≤ 100);接下来 n 行,每行两个整数。
输出格式n 行,每行一个整数。

输入

3
1 2
-1 5
0 0

输出

3
4
0
三个点的坐标和分别是 3、4、0。
这一版是错的
#include <bits/stdc++.h>
using namespace std;

struct Point {
    int x, y;
}

int main() {
    Point p = {1, 2}
    cout << p.x + p.y << "\n";
    return 0;
}
g++ 说
b1.cpp:6:2: error: expected ‘;’ after struct definition
    6 | }
      |  ^
      |  ;
b1.cpp: In function ‘int main()’:
b1.cpp:10:5: error: expected ‘,’ or ‘;’ before ‘cout’
   10 |     cout << p.x + p.y << "\n";
      |     ^~~~
先想一想

两处都漏了分号,而编译器报的行号一处都不在漏分号的那一行上。先自己指出:这两条报错分别在说哪一行的问题?

errEx1.cpp参考代码
// 例题 ①「漏了分号」改好之后的版本 —— 报错了怎么读
//
// 两处漏分号,而编译器报的行号**一处都不在出错的那一行上**:
// struct 那个报在花括号所在行(第 6 行),语句那个报在**下一行**(第 10 行)。
// ⇒ 看到 expected ';' 一律先往上看一行。
#include <bits/stdc++.h>
using namespace std;
struct Point {
int x, y;
}; // ← struct / class 的花括号后面**必须**有分号
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
Point p;
cin >> p.x >> p.y; // ← 每条语句结尾的分号,同理
cout << p.x + p.y << "\n";
}
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
这段代码是怎么想出来的
  1. 第一条 expected ';' after struct definition 指在第 6 行(struct 的右花括号那行)——这一条倒是指对了:struct / class 的花括号后面必须有分号。⚠ 一般的花括号(iffor、函数)结尾不用分号,唯独这两个要,所以特别容易漏。
  2. 第二条 expected ',' or ';' before 'cout' 指在第 10 行,可真正漏分号的是第 9 行Point p = {1, 2})。⇒ 因为编译器是读到下一行才发现「上一句还没写完」。
  3. ★★ 所以这一组的第二条通用读法是:报错的行号常常指到出错行的下一行,看到行号先往上看一行。
  4. ★ 第一条通用读法是:只看第一条错误,改完再编译一遍。C++ 的编译错误会「雪崩」—— 前面一个分号能引发后面几十条,而那几十条全是噪声。
记住这一句

改好之后顺手记住这个手势:写完 struct 的右花括号,手指自动敲一个分号。

用到的写法:expected ';' before ...

例题 ② max 的两个参数类型必须一样

errEx2.cpp

下面这段程序编译不过。请读懂报错,改好它。

改好之后它该做的事是:读一个 int n 和一个 long long m,第一行输出两者中较大的,第二行输出较小的。

输入格式一行两个整数 n|n| ≤ 10⁹)和 m|m| ≤ 10¹⁸)。
输出格式两行,分别是较大值和较小值。

输入

3 1000000000

输出

1000000000
3
31000000000 里较大的是后者,较小的是 3。
这一版是错的
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n = 3;
    long long total = 1000000000LL;
    cout << max(n, total) << "\n";
    return 0;
}
g++ 说(只留了开头几行)
b2.cpp: In function ‘int main()’:
b2.cpp:7:16: error: no matching function for call to ‘max(int&, long long int&)’
    7 |     cout << max(n, total) << "\n";
      |             ~~~^~~~~~~~~~
b2.cpp:7:16: note: there are 4 candidates
… (后面跟着一长串 candidate: 和 template argument deduction/substitution failed)
先想一想

no matching function for call to … 的意思是「函数名对,但参数对不上」。报错里那个 max(int&, long long int&) 已经把原因写出来了 —— 两个参数的类型不一样。

errEx2.cpp参考代码
// 例题 ②「max 的两个参数类型必须一样」改好之后的版本 —— 报错了怎么读
//
// no matching function for call to 'max(int&, long long int&)'
// = 函数名对,但**参数对不上**。
// max 要求两个参数类型完全一样,int 和 long long 混着传就找不到匹配的版本。
// 两条改法都行:把 int 那个转过去,或者直接指定类型 max<long long>(n, m)。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
long long m;
cin >> n >> m;
cout << max((long long)n, m) << "\n"; // 改法一:把两边凑成同一个类型
cout << min<long long>(n, m) << "\n"; // 改法二:直接告诉它按哪个类型比
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
这段代码是怎么想出来的
  1. max 要求两个参数的类型完全一样intlong long 混着传,它推不出该用哪一个类型,于是「找不到匹配的版本」。
  2. ⚠ 报错后面会跟一长串 candidate: —— 那是它试过的所有版本。不用逐条看,直接回去核对自己传的参数类型就行。
  3. 改法一:把两边凑成同一个类型 —— max((long long)n, m)
  4. 改法二:直接告诉它按哪个类型比 —— max<long long>(n, m)
  5. ⚠ 顺带一个同类的:max(1, 2.0) 也不行(int 和 double)。⇒ 看到 no matching function,第一反应就是去数参数的个数类型
记住这一句

这是新手最常撞的一条编译错误,而它的根子不是 max 有问题,是 C++ 不会替你猜「这两个不同类型的东西该按哪个来比」。

用到的写法:no matching function for call to ...

练习(3 道)

答案折叠着,先自己写一版。

练习 ① 'sum' was not declared in this scope

errTry1.cpp

下面这段程序编译不过。请读懂报错,改好它。

改好之后它该做的事是:读一个正整数 n,输出 1 ~ n 里所有偶数的和。

输入格式一行一个正整数 n1 ≤ n ≤ 10⁵)。
输出格式一行一个整数。

输入

10

输出

30
2 + 4 + 6 + 8 + 10 = 30
这一版是错的
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int sum = 0;
        sum += i;
    }
    cout << sum << "\n";
    return 0;
}
g++ 说
b3.cpp: In function ‘int main()’:
b3.cpp:11:13: error: ‘sum’ was not declared in this scope
   11 |     cout << sum << "\n";
      |             ^~~
提示

这句话的意思是「这个名字编译器不认识」,按可能性从高到低查三件事:① 拼错了;② 作用域不对(它定义在某对花括号里面,出了那对括号就不存在了);③ 忘了 #include(本书一律用万能头,所以基本不是这个原因)。⚠ 这道题是第 ② 种 —— 而且改对之后还有一个 bug 要顺手修掉(题目要的是偶数的和)。

errTry1.cpp参考答案
记住这一句

⚠ 还有一种最气人的「not declared」:在函数 A 里调用了写在它下面的函数 B。C++ 是从上往下读的 ⇒ 要么把 B 挪到 A 前面,要么在最上面先写一行声明。

用到的写法:'xxx' was not declared in this scope

练习 ② Segmentation fault

errTry2.cpp

下面这段程序编译得干干净净-Wall 一条警告都没有),可一跑就崩。请找出原因并改好。

它该做的事是:读一个正整数 n,把 a[1] … a[n] 依次填成 1 … n,然后输出 a[n]

输入格式一行一个正整数 n1 ≤ n ≤ 10⁶)。
输出格式一行一个整数。

输入

1000000

输出

1000000
n = 10⁶ 时答案就是 1000000 —— 前提是数组装得下。
这一版是错的
#include <bits/stdc++.h>
using namespace std;

const int N = 10;
int a[N];

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) a[i] = i;
    cout << a[n] << "\n";
    return 0;
}
真的跑一遍
$ g++ -std=c++17 -O2 -Wall -o b6 b6.cpp
(没有任何输出 —— 零错误、零警告)
$ echo 1000000 | ./b6
退出码 = 139
提示

退出码 139 就是 Segmentation fault(139 = 128 + 11,11 是 SIGSEGV)。⚠ 越界访问编译期一句话都不会说 —— 查的顺序是:① 数组开够了没;② 下标算出来会不会是负的;③ 递归有没有出口、会不会太深。

errTry2.cpp参考答案
记住这一句

⚠ 最快的定位法:把可疑的 a[i] 临时换成 a.at(i)(vector 才有),越界会当场抛异常并告诉你下标是多少,而不是让程序在别处崩。查完记得换回去,at 更慢。★ 而这道题真正该留下的习惯是:数组开多大,是照题面的上限写的,不是照样例写的。

用到的写法:Segmentation fault(运行时) const int N = 100005;

练习 ③ 第一组对,第二组起全错

errTry3.cpp

下面这段程序编译得过、也不崩,样例的第一行还是对的 —— 但第二行开始就错了。请找出原因并改好。

它该做的事是:第一行一个整数 T,接下来 T 组数据,每组第一个数是 n,后面跟着 n 个整数。对每组输出这 n 个数的和。

输入格式第一行 T1 ≤ T ≤ 100);接下来 T 组,每组一个 n1 ≤ n ≤ 1000)和 n 个整数(|aᵢ| ≤ 10⁹)。
输出格式T 行,每行一个整数。

输入

2
3 1 2 3
2 10 20

输出

6
30
第一组 1+2+3 = 6,第二组 10+20 = 30
这一版是错的
#include <bits/stdc++.h>
using namespace std;

int main() {
    int T;
    cin >> T;
    long long sum = 0;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            int x;
            cin >> x;
            sum += x;
        }
        cout << sum << "\n";
    }
    return 0;
}
真的跑一遍(第二行本该是 30)
$ printf '2\n3 1 2 3\n2 10 20\n' | ./b7
6
36
提示

第一组对、第二组起全错 —— 这个症状本身就是答案。问一句:那个攒着的量,是在哪儿被清干净的

errTry3.cpp参考答案
记住这一句

这是多组数据的题里排第一的 bug,而它还有一个更隐蔽的版本:全局数组只在程序启动时清过一次,多组数据之间必须自己清。⇒ 判据很直白:凡是「攒着的东西」(累加器、访问标记、邻接表),都要问一句它在哪儿归零。

用到的写法:编译过了,可答案不对 'xxx' was not declared in this scope

做完了去哪儿

← 上一组:零碎但天天用的那几样 · 回 C++ 速查 · 这是最后一组