エラー

概要

プログラムを書いたときに、うまく動かないことがある。
そのときに表示されるエラーメッセージや実際の挙動を手がかりに、原因を探して直す必要がある。

競技プログラミングでよく見るエラーは、大きく分けると以下の $3$ つ。

なお、このページでは C++23 の gcc で出るメッセージを想定する。

コンパイルエラー CE

コンパイルエラーは、C++ のコードを機械語に翻訳する段階で失敗した場合のエラー。
何かを書き忘れているか、余計なものを書いている場合に発生しやすい。

AtCoder の場合、提出で CE を出してもペナルティにはならない。

エラーメッセージの最初に、次のような表示が出ることがある。

./Main.cpp: In function ‘int main()’:
./Main.cpp:18:3: error:

これは、main() 関数内の、全体で $18$ 行目、$3$ 文字目あたりにエラーがあるという意味。
ただし、本当に悪い場所はその行ではなく、$1$ つ前の行の末尾であることもある。

たくさんエラーが出た場合は、まず最初のエラーから直す。
$2$ つ目以降のエラーは、最初のエラーの影響で連鎖的に発生しているだけのことが多い。

最初のエラー場所が ./Main.cpp でない場合は、./Main.cpp での場所が初めて出てくるところを探す。
このような場合は、標準ライブラリの中でエラーが起きているかのように見えているだけ。
実際には自分のコードでライブラリを呼び出した場所に原因があることが多い。

./Main.cpp:18:3: warning:

error ではなく warning と書かれている場合、それは警告である。
これは、「実行しようと思えばできるが、怪しいところがある」というメッセージ。
内容を確認して、問題なさそうなら無視してもよい。
ただし、本当に何かミスをしていることの方が多い。

以下に、よくあるコンパイルエラーを示す。

未宣言の変数を使っている場合

./Main.cpp:18:3: error: ‘a’ was not declared in this scope
   18 |   a = 0;
      |   ^

not declared と書かれている場合、宣言されていない変数や名前を使っている。
単純な宣言忘れ、変数名のタイポ、スコープのミスなどが原因になる。

未宣言の型を使っている場合

./Main.cpp:4:1: error: ‘Foo’ does not name a type
    4 | Foo x;
      | ^~~

does not name a type と書かれている場合、存在しない型を使おうとしている。
型名のタイポや、型を定義する前に使っていることが原因になりやすい。

std:: の後ろの名前を間違えた場合

./Main.cpp:13:8: error: ‘a’ is not a member of ‘std’
   13 |   std::a;
      |        ^

not a member of std と書かれている場合、std:: の後ろに存在しない名前を書いている。
using namespace std; を使っている場合はあまり出ないが、標準ライブラリ名のタイポで起きる。

同じ名前のものを重複して宣言した場合

./Main.cpp:22:7: error: redeclaration of 'int a'
   22 |   int a = 1;
      |       ^

redeclaration と書かれている場合、同じ名前の変数などを $2$ 回宣言している。
片方の名前を変えるなどして直す。

関数の引数の型がおかしい場合

./Main.cpp:15:6: error: no matching function for call to ‘max(int&, double&)’
   15 |   max(n,d);
      |   ~~~^~~~~

no matching function と書かれている場合、その引数を受け取れる関数が見つかっていない。
この例では、max()int 型と double 型を渡している。
しかし、max() は基本的に同じ型の値 $2$ つを受け取る関数である。
型変換などで、引数の型を合わせる。

関数の引数の個数がおかしい場合

./Main.cpp:19:4: error: too few arguments to function ‘void f(int, int)’
./Main.cpp:19:4: error: too many arguments to function ‘void f(int, int)’

too few arguments は引数が少なすぎる、too many arguments は引数が多すぎるという意味。
関数定義や標準関数の使い方を確認し、渡す引数の個数を合わせる。

型の暗黙変換に失敗した場合

./Main.cpp:17:5: error: could not convert ‘n’ from ‘int’ to ‘std::string’

could not convert と書かれている場合、型変換に失敗している。
例えば、string 型を受け取る関数に int 型の変数を渡すと、このようなエラーになる。

参照渡しに一時値を渡した場合

./Main.cpp:17:6: error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
   17 |   f(n+1);
      |     ~^~

cannot bind と書かれている場合、参照渡しに失敗している。
参照渡しで受け取る関数に、変数そのものではない一時値を渡している場合など。
一度別の変数に入れてから渡すか、関数側を const 参照や値渡しにする。

セミコロンを忘れた場合

./Main.cpp:19:3: error: expected ‘,’ or ‘;’ before ‘int’
   19 |   int b = 0;
      |   ^~~

expected と書かれている場合、何か必要なものを書き忘れている。
この例では、実際には $18$ 行目の末尾の ; を忘れたせいで、次の行の先頭でエラーしている。

括弧の対応がおかしい場合

./Main.cpp:26:12: error: expected ‘}’ at end of input
   26 |   return 0;
      |

expected ‘}’ は、閉じ括弧 } が足りないという意味。
ただし、括弧を忘れた場所そのものに表示が出るとは限らない。
全体を見て、{} の対応を確認する。

閉じ括弧 } が多すぎる場合は、次のような表示になることもある。

./Main.cpp:30:1: error: expected declaration before ‘}’ token
   30 | }
      | ^

引用符の対応がおかしい場合

./Main.cpp:21:14: error: missing terminating " character
   21 |   string s = "abc;
      |              ^~~~~

missing terminating " character は、文字列の引用符 " を閉じ忘れているという意味。
文字列リテラルの前後を確認する。

式の書き方が正しくない場合

./Main.cpp:20:9: error: expected primary-expression before ‘/’ token
   20 |   a = b+/c;
      |         ^

expected primary-expression は、式を書くべきところに式ではないものを書いた場合に出る。
演算子の前後に値を書き忘れていないか、関数の引数が空になっていないかなどを確認する。

型が合わない値を代入した場合

./Main.cpp:22:7: error: invalid conversion from 'const char*' to 'int'
   22 |   a = "hoge";
      |       ^~~~~~

invalid conversion は、不正な型変換という意味。
この例では、int 型の変数に文字列を代入しようとしている。

添字アクセスできないものに添字アクセスした場合

./Main.cpp:22:4: error: invalid types 'int[int]' for array subscript
   22 |   a[0];
      |    ^

invalid types ... for array subscript と書かれている場合、誤った [] の使い方をしている。
この例では、int 型に対して [] を使ってしまっている。
変数の型が意図通りか確認する。

存在しないメンバにアクセスした場合

./Main.cpp:21:9: error: ‘class std::vector<int>’ has no member named ‘hoge’
   21 |   a.hoge();
      |     ^~~~

has no member と書かれている場合、存在しないメンバ関数やメンバ変数を使っている。
メンバ名のタイポや、変数の型の間違いを疑う。

クラスではないものにメンバアクセスした場合

./Main.cpp:20:9: error: request for member ‘size’ in ‘n’, which is of non-class type ‘int’
   20 |   n.size();
      |     ^~~~

request for member と書かれている場合、メンバを持たない型に対して .size() などを使っている。
non-class type と書かれる場合もある。
この例では、nint 型なので、.size() は存在しない。

使えない演算子を使った場合

./Main.cpp:21:9: error: no match for ‘operator+’
   21 |   b = a + n;
      |       ~~^~~

no match for operator+ のように書かれている場合、その型の組み合わせではその演算子が使えない。
変数の型を確認し、正しい演算子や型に直す。

<...> の中の項目数を間違えた場合

./Main.cpp:19:11: error: wrong number of template arguments (1, should be 2)
   19 |   pair<int> a;
      |           ^

wrong number of template arguments は、テンプレート引数の個数が違うという意味。
例えば pair 型では、pair<int,int> のように $2$ つの型を書く必要がある。

型を書く場所に値を書いた場合

./Main.cpp:19:11: error: type/value mismatch at argument 1 in template parameter list
   19 |   vector<n> a;
      |           ^

type/value mismatch は、型を書くべきところに値を書いている場合などに出る。
この例では、vector<...> の中に型ではなく変数 n を書いている。

定数を書く場所に変数を書いた場合

./Main.cpp:19:11: error: the value of 'n' is not usable in a constant expression
   19 |   bitset<n> a;
      |           ^

not usable in a constant expression は、定数が必要な場所に、変数を書いているという意味。
例えば、bitset<...> の中にはコンパイル時に定まっている定数を書く必要がある。

関数の宣言だけして中身を作り忘れた場合

undefined reference to `f(int)'
collect2: error: ld returned 1 exit status

undefined reference と書かれている場合、関数の宣言だけして、定義を書き忘れていることがある。
宣言だけでなく、中身も作る。

main 関数を作り忘れた場合

undefined reference to `cpp_main(int, char**)'
collect2: error: ld returned 1 exit status

上の undefined reference の特殊なパターン。
main() 関数を作り忘れている。
ただし、エラーメッセージの文言は環境による。

#includeusing namespace std; を書き忘れた場合

./Main.cpp:11:3: error: 'cin' was not declared in this scope; did you mean 'std::cin'?
   11 |   cin >> n;
      |   ^~~

not declared と言われた場合、#includeusing namespace std; を書き忘れている可能性がある。
using namespace std; を使わないなら、std::cin のように std:: を付ける必要がある。

全角文字を使った場合

./Main.cpp:18:3: error: extended character   is not valid in an identifier

extended character と書かれている場合、全角スペースなどの全角文字が混ざっている可能性が高い。
全角文字を消し、半角スペースやタブに直す。

よくある警告

宣言した変数が未使用な場合

./Main.cpp:21:7: warning: unused variable 'a'
   21 |   int a = 0;
      |       ^

unused variable は、宣言したのに使っていない変数があるという意味。
単に消し忘れただけなら無視してよいが、その変数を使う処理を書き忘れている可能性もある。

符号が異なる整数型を比較した場合

warning: comparison of integer expressions of different signedness

different signedness と書かれている場合、符号あり整数と符号なし整数を比較している。
int 型の変数と、vector.size() の返り値を比較したときに出やすい。
問題なく動くことも多いが、意図通りか確認しておく。

返り値がある関数で return を忘れた場合

warning: no return statement in function returning non-void

no return statement は、返り値が必要な関数で return を書き忘れているという意味。
void 型でない自作関数で出た場合は、基本的に直す。

実行時エラー RE

実行時エラーは、コンパイルは通ったが、実行中に処理を続けられない問題が起きた場合のエラー。
原因がメモリ制限超過の場合は、RE ではなく MLE 判定になることがある。

.at() で範囲外にアクセスした場合

terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 18446744073709551615) >= this->size() (which is 5)

out_of_range と書かれている場合、vectordeque などの範囲外にアクセスしている。
which is の $1$ つ目がアクセスしようとした位置、$2$ つ目が実際の長さ。

上の例では、長さ $5$ の vector に対して、範囲外の位置を見ようとしている。
18446744073709551615 のような巨大な値は、実際には -1 番目にアクセスしたときに出ることがある。

なお、.at() ではなく [] を使った場合、このエラーは出ない。
ただし、それはバグがないという意味ではない。
エラーを出さないまま誤動作をする可能性があるという意味であり、不具合が修正しにくいだけである。

ゼロ除算をした場合

コードテストでの終了コードは 136 になることがある。
エラーメッセージは出ない。

0 で割る処理が発生しているので、コード中の割り算を全て確認する。

メモリを使いすぎた場合

terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc

bad_alloc と書かれている場合、新しいデータ用のメモリ確保に失敗している。
巨大な vector を作った場合などに発生する。
データの持ち方を変え、メモリ使用量を減らす必要がある。

論理エラー

論理エラーは、実行は終了したが、結果が意図通りになっていないエラー。
また、各行では異常な処理が起きていないものの、無限ループで処理が終わらない場合もある。

コンピューターは、書かれている通りにしか動かない。
頭の中のコード通りに書けていないか、頭の中のコードがそもそも間違っている。
WA や、計算量的には間に合うはずなのに TLE になった場合は、論理エラーを疑う。

無限ループが発生している場合

for ループや while ループの終了条件を間違えると、処理が終わらないことがある。
コードテスト上で実行時間が極端に長くなったり、提出時に TLE になったりする。

計算量に問題がないはずなのに TLE する場合、無限ループの可能性がある。
ループの先頭で cerr などを使ってメッセージを出してみると、それが大量に出続ける挙動をする。

何かを書き忘れた場合

ソートし忘れた、カウンターを増やし忘れた、答え用の変数に反映し忘れた、など。
小さいテストケースで cerr などを使い、変数の中身が想定通りに変わっているか確認する。

初期化がおかしい場合

カウンターの 0 初期化を忘れた、最小値を探すのに -1 で初期化した、など。
適当な大きい値で初期化したが、本当の答えがそれより大きい場合もある。

使う変数を間違えた場合

vector の長さとして m を使うべきところで n を使った、など。
入力変数に極端な値を入れてみると、変な挙動が出て原因を見つけやすい。

比較演算子や条件式を間違えた場合

== を誤って = にした、不等号の向きを逆にした、<=< を間違えた、&&|| を間違えた、など。
境界値テストや、複数条件のうち $1$ つだけ成り立つ入力を試すと見つけやすい。

0-indexed1-indexed が混ざった場合

プログラム上の配列は、多くの場合 $0$ 番目から始まる。
一方で、問題文では数列を $1$ 番目から数えることが多い。

問題文の「$K$ 番目」を、そのままプログラムの K 番目として扱うと、見る場所が $1$ つずれる。
端のデータを使うテストをすると見つけやすい。

小数の扱いに問題がある場合

本来等しいはずの値が、丸め誤差で少しずれて ==false になることがある。
また、整数として正確に扱いたい値を double 型で持つと、一の位が狂うこともある。

小数を使わずに整数で処理できないか、設計時点で考える。
どうしても小数を使う場合は、誤差を意識して比較や出力を行う。

割り算の扱いに問題がある場合

小数で答えるべきところで int 型同士の割り算をしてしまう、など。
また、負数の除算や余りは、数学で想定しているものとずれることがある。
割り算に負数や小数が絡む場合は、特に注意して確認する。

オーバーフローした場合

int 型は、およそ $21$ 億程度までしか扱えない。
入力が $10$ 億程度の場合、$3$ つ足すだけで範囲を超える。
また、$10^5$ 程度の数同士を掛け算しても範囲を超える。

サンプルは小さい数であることが多い。
サンプルは通るのに提出で WA になる場合、オーバーフローを疑う。
計算途中で値が最大になるケースを手計算で確認するのがよい。

コーナーケースを見落とした場合

特殊な入力だけ別処理が必要な場合がある。
ほとんど AC して $1$ ケースや $2$ ケースだけ WA になるときは、この可能性がある。
境界値のテストをしてみるとよい。

そもそもアルゴリズムが間違っている場合

「このアルゴリズムで解けるはず」という考え自体が間違っていることもある。
特に、誤った貪欲法や動的計画法を使ってしまう形で起きやすい。

愚直解も作り、小さい入力のランダムテストで答えを比べると見つけられる場合がある。
ただし、愚直解を書くこと自体に時間がかかる。

エラーの直し方

エラーを直すときの流れ

まず、エラーメッセージが出ているか確認する。
出ているなら、最初の error から読む。

warning だけの場合は実行できることもあるが、怪しいところのヒントにはなる。
内容を確認して、想定通りか判断する。

RE の場合、エラーメッセージから原因が分かることもある。
しかし、どの行で発生したかまでは分からない場合も多い。
配列の長さ、割り算、メモリ使用量など、起きやすい原因から確認する。

エラーメッセージがなく、出力だけがおかしい場合は論理エラーを疑う。
小さい入力を作り、途中の変数を cerr で出しながら確認する。

テストで原因を探す

論理エラーは、エラーメッセージが出ないことが多い。
そのため、どこで想定と違う値になっているかをテストで確認する。

まずは小さい入力を手で作り、各変数の値が思った通りに変化しているか見る。
次に、最小値、最大値、境界値、コーナーケースを試す。

必要であれば、愚直解と本解の両方を作り、小さいランダム入力で答えを比較する。

cerr で途中経過を見る

途中経過を確認したい場合は、cerr が使える。
cerr で出した内容は、通常の解答出力とは別に扱われる。

ただし、大量に出力するとそれ自体が遅くなり、TLE の原因になることがある。
必要な情報だけを出す。

注意点

最初のエラーから直す

エラーが大量に出ても、最初の $1$ つだけが本当の原因であることが多い。
後ろのエラーから見始めると、原因ではない場所を探し続けることになる。

表示された場所が本当の原因とは限らない

セミコロン忘れや括弧忘れでは、表示された行より前に原因があることが多い。
^ の位置だけでなく、その直前の行や周辺の文も確認する。

[] は範囲外アクセスを教えてくれない

vector などを .at() で見ると、範囲外アクセス時に out_of_range が出ることがある。
一方で、[] で範囲外アクセスをしても、分かりやすいエラーが出るとは限らない。

[] でエラーが出ていないから正しい、とは思わないこと。

サンプルが通っても正しいとは限らない

オーバーフロー、コーナーケース、添字のずれ、小数誤差などは、サンプルでは表に出ないことがある。
自分で境界値や極端な入力を試すこと。

関連知識

C++のコードの書き方

セミコロン、括弧、コメント、全角文字など、コードの基本的な書き方は、エラーの原因と直結する。

入出力

入力の個数や順番がずれると、後続の処理が想定通りに動かず、論理エラーの原因になる。

テスト

論理エラーは、テストを用いて原因を探す。
小さい入力、境界値、コーナーケース、ランダムテストを使い分ける。

コーナーケース

特殊な入力だけ別処理が必要な問題では、コーナーケースの見落としで WA になることがある。

int型

int 型は、およそ $21$ 億程度までしか扱えない。
オーバーフローの原因になりやすい。

long long型

long long 型を使うと、int 型より大きな整数を扱える。
ただし、long long 型でもはみ出る場合がある。

double型

小数は丸め誤差が発生する。
等値判定や整数への変換には注意が必要。

vector

vector の範囲外アクセスは、実行時エラーや論理エラーの原因になる。