アットウィキロゴ
競技プログラミング用 知識集積所
掲示板 掲示板 ページ検索 ページ検索 メニュー メニュー

競技プログラミング用 知識集積所

エラー

最終更新:

sport_programming

- view
管理者のみ編集可


雑な説明

プログラムを書いたときに、うまく動かないことがある。
そのときに、エラーを表示してくれたり、してくれなかったり、をヒントにどうにか修正しなければならない。
そのための知識集。
難度を問わず掲載(C++23のgccでの情報のみ)。

コンパイルエラー CE

コンパイルに失敗した場合のエラー
つまり、C++から機械語に翻訳するのに失敗した場合のエラー
多くの場合、何か必要なものの書き忘れか、余計なことを書いてあるせいで発生する。

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

エラーメッセージの見方

標準エラー出力の最初に
./Main.cpp: In function ‘int main()’:
./Main.cpp:18:3: error:
と書いてあった場合、main()関数内、全体で18行目の3文字目にエラーがある。
多くの場合はここに何か書き忘れているか、余計なことが書いてある。
その行の先頭にエラーがあると表示されている場合は、本当にエラーしているのは1つ前の行の末尾であることもある。

たくさんエラーが出た場合は、まずは最初のエラーを解決する(超重要)
というのも、2つ目以降のエラーは1つ目のエラーの影響で発生しているだけということも多く、その場合はもちろん2つ目以降のエラーの原因を探すのは無駄な努力であるため。

また、1つ目のエラー場所が./Main.cppでない場合、./Main.cppでの場所が初めて出てくる場所を探す。
ライブラリの使い方が正しくなかったせいでライブラリ内でエラーが発生した場合、
エラーしたライブラリとその場所
↑を呼び出したライブラリとその場所
↑を呼び出したライブラリとその場所
↑を呼び出したライブラリとその場所
↑を呼び出した./Main.cppの場所
のような表示になるため。

エラーとワーニング

標準エラー出力のメッセージが
./Main.cpp:18:3: error:
ではなく
./Main.cpp:18:3: warning:
であることがある。
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(名付けていない)と書かれている場合、まだ存在しない型を使おうとしている。
原因は、型を作る前に使ってしまった場合もあるし、タイポということもある。
ただし、変な型を使った場合でも、未宣言の変数と解釈されて、1つ上の項目のエラーが出る場合もある。

stdの何かの名前を間違えた場合

./Main.cpp:13:8: error: ‘a’ is not a member of ‘std’
   13 |   std::a;
      |        ^
not a member of ‘std’(stdのメンバーではない)と書かれている場合、std::のうしろに変な名前を書いてしまっている。
using namespace std; を使って std:: の記述を略している場合は普通は発生しない。

以下のようにstdではなく自作の何かで類似のエラーが出た場合、
./Main.cpp:15:9: error: ‘X’ is not a member of ‘Hoge’
   15 |   Hoge::X a;
      |    
そもそものHogeの方の実装ミスや記述の可能性もあるため、そちらも確認する。

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

./Main.cpp:22:7: error: redeclaration of 'int a'
   22 |   int a = 1;
      |       ^
redeclaration(再宣言)と書かれている場合、同じ名前のものを2回宣言している。
上の例では、既にaという変数が存在するのに、さらにaを作ろうとしている。
片方の変数名を変えること。
なお、異なるブロックでの重複はコンパイルエラーしない。
とはいえ、予期しない挙動の原因なので、それも注意して人力で避ける。

よくあるコンパイルエラーメッセージ(関数呼び出し系)

引数の型がおかしい場合

./Main.cpp:15:6: error: no matching function for call to ‘max(int&, double&)’
   15 |   max(n,d);
      |   ~~~^~~~~
no matching function(合う関数がない)と書かれている場合、その引数を受け取れる関数がない。
この例ではint型のnとdouble型のdをmax()※に渡そうとしているが、この関数は同じ型の変数2つを渡すのが正しいため、エラーとなってしまった。
型変換※などで適切な型で渡すように修正する。
そのエラーメッセージの下に note: candidate という表示があり、正しい引数の型が表示されているが、見にくい場合も多い。
実際のミスが個数の間違いでも、別の型で渡せばその個数で動く同名の関数があったためにこっちのエラーが出ていることもある。

逆に、合う関数が複数ある状況ではambiguous(曖昧な)と書かれる場合もある。

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

./Main.cpp:19:4: error: too few arguments to function ‘void f(int, int)’
   19 |   f(n);
      |   ~^~~

./Main.cpp:19:4: error: too many arguments to function ‘void f(int, int)’
   19 |   f(l,m,n);
      |   ~^~~~~~~
too few arguments(引数が少なすぎる)、too many arguments(引数が多すぎる)と書かれている場合、引数の個数を間違えている。
この例では、int型の引数2つを取る関数fに、引数を1つだけ、あるいは3つも渡してしまい、エラーとなってしまった。
渡すべき引数の個数を再確認して修正する。

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

./Main.cpp:17:5: error: could not convert ‘n’ from ‘int’ to ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’}
   17 |   f(n);
      |     ^
      |     |
      |     int
could not convert(変換できない)と書かれている場合、型の暗黙変換に失敗している。
例えばlong long型を受け取る関数にint型を渡すと、自動的にlong long型に変換して動作してくれる。
しかし、この例では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(割り当てできない)と書かれている場合、参照渡しで受け取る関数に、一時値などを渡してしまっている。
この例では、fはint&で引数を取るが、n+1は変数そのものではないので、エラーとなってしまった。
一度変数に入れてから渡すか、変更の予定がないなら関数側でconstで受け取るか値渡しにするか、に修正する。

よくあるコンパイルエラーメッセージ(構文エラー系)

セミコロンを忘れた場合

./Main.cpp:19:3: error: expected ‘,’ or ‘;’ before ‘int’
   19 |   int b = 0;
      |   ^~~
expected(期待される)と書かれている場合、何か必要なものを書き忘れている。
この場合、18行目の末のセミコロンを忘れたために、19行目の先頭でエラーしている。
このタイプのエラーでは、^の位置ではなくその直前がエラー場所なので注意。
expectedの後ろに書かれているものを書き足して修正すること。
ただし、コンパイラが自分の書きたいものとは違うコードを想定してメッセージを出している場合もあるので、内容もきちんと確認すること。

括弧の不整合がある場合

./Main.cpp:26:12: error: expected ‘}’ at end of input
   26 |   return 0;
      |   
上と同じく、expected(期待される)と書かれているので、必要なものを書き忘れている。
ただし、括弧を忘れたその場がエラーしているとは限らない(間違った組み合わせで括弧の対応がとられてしまっている)。
全体をよく見て足りない場所を慎重に探すこと。
VSCodeなどを使っている場合、{}の不整合には色付けや自動整形を使ってみるのも手。

また、{}の閉じる方が多い場合のエラーメッセージは以下。
./Main.cpp:30:1: error: expected declaration before ‘}’ token
   30 | }
      | ^

引用符の不整合がある場合

./Main.cpp:21:14: error: missing terminating " character
   21 |   string s = "abc;
      |              ^~~~~    
括弧ではなく引用符を閉じ忘れた場合。
エラーメッセージが括弧の場合とは異なる。
全体をよく見て足りない場所を慎重に探すこと。

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

./Main.cpp:20:9: error: expected primary-expression before ‘/’ token
   20 |   a = b+/c;
      |         ^
expected primary-expression(基本式が期待される)と書いてある場合、式を書くべきところに式ではない何かを書いてしまっている、または空欄になってしまっている。
タイポ等で変な記述になっているので、よく見直して修正する。
とりあえず疑うのは演算子の前後に変数書き忘れ、関数の引数が空になっている、の2つだが、他の原因のこともある。

よくあるコンパイルエラーメッセージ(型エラー系)

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

./Main.cpp:22:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
   22 |   a = "hoge";
      |       ^~~~~~
      |       |
      |       const char*
invalid conversion(不正な変換)と書かれている場合、型が合わずに値の代入に失敗している。
C++ではlong long型変数にint型の値を代入しようとすると、自動で型が合うように変換してくれる。
しかし上の例のように、int型に文字列を代入するのは変換不可能であり、このようにエラーする。

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

./Main.cpp:22:4: error: invalid types 'int[int]' for array subscript
   22 |   a[0];
      |    ^
invalid types ... for array subscript (添字アクセス不正な型)と書かれている場合、[ ]を使ったアクセスができないものに[ ]を使ってしまっている。
上の例では、aはint型なのに、そこに[ ]でアクセスしようとしてしまっている。
型が意図した通りになっているか確認すること。

変なメンバにアクセスした場合

./Main.cpp:21:9: error: ‘class std::[[vector]]<int>’ has no member named ‘hoge’
   21 |   s = a.hoge();
      |         ^~~~
has no member(メンバが存在しない)と書かれている場合、存在しないメンバを利用しようとしている。
この例では、aはvectorというクラスであるが、vectorに.hoge()というメンバーは存在しない。
メンバの名前を間違えている可能性が高く、変数の型(クラス)を間違えている場合もある。
※下の項目と違い、クラスではあるが、APIがおかしい

クラスじゃないのにメンバアクセスした場合

./Main.cpp:20:9: error: request for member ‘size’ in ‘n’, which is of non-class type ‘int’
   20 |   a = n.size();
      |         ^~~~
request for member(メンバ要求)、which is of non-class type(クラスではない)と書かれている場合、クラスではない(つまりメンバを持たない)変数のメンバを利用しようとしている。
この例では、nはint型であり、クラスではないため、当然.size()などというメンバは存在しない。
変数の型を間違えている可能性が高い。
※上の項目と違い、そもそもクラスでない

変な演算子を使った場合

./Main.cpp:21:9: error: no match for ‘operator+’ (operand types are ‘std::vector<int>’ and ‘int’)
   21 |   b = a + n;
      |       ~ ^ ~
      |       |   |
      |       |   int
      |       std::vector<int>
no match for ‘operator+'(+という演算子に適合しない)と書かれている場合、演算子の使い方を誤っている。
(実際には、‘operator+'の+の代わりに今回適合できなかった演算子が書き込まれている)
この場合、vectorであるaとint型であるnを足そうとしたためにエラーとなってしまった。
自分のコードをよく見て、変数の型を確認し、正しい演算子に直すか、正しい変数の型に直すか、などで修正する。

no match for ‘operator=' の場合には、const※など代入禁止の変数に対して代入しようとしている場合もある。

よくあるコンパイルエラーメッセージ(テンプレート系)

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

./Main.cpp:19:11: error: wrong number of template arguments (1, should be 2)
   19 |   pair<int> a;
      |           ^
wrong number of template arguments(テンプレート引数の個数が違う)は、<...>の中の項目数を間違えている。
上の例では、pair型※は<...>の中に2つの型を書かなくてはならないのに、1つしか書かないミスをしている。
<...>の中身をよく確認すること。

型と値を間違えた場合

./Main.cpp:19:11: error: type/value mismatch at argument 1 in template parameter list for 'template<class _Tp, class _Alloc> class std::vector'
   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※の後ろの<...>は、定数を書かなければならず、変数は使えない。
にもかかわらず、nという変数名を書いてしまっている。
<...>の中に値を書くときには、コンパイルの時点で確定している定数で書くこと。

よくあるコンパイルエラーメッセージ(リンクエラー系)

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

/usr/bin/ld: /tmp/ccuLFZXx.ltrans0.ltrans.o: in function `main':
<artificial>:(.text.startup+0x20): undefined reference to `f(int)'
collect2: error: ld returned 1 exit status
undefined reference to ...(...への参照が未定義)は、関数の宣言だけして中身を作り忘れている。
上の例では、f(int n)という関数の宣言だけして、中身を作り忘れている。
きちんと作ること。

main関数を作り忘れた場合

/usr/bin/ld: /opt/atcoder/gcc/lib/libboost_prg_exec_monitor.a(cpp_main.o): in function `main':
cpp_main.cpp:(.text.startup+0x6): undefined reference to `cpp_main(int, char**)'
collect2: error: ld returned 1 exit status
上の項目の特殊パターン。
main()関数を作り忘れている。

よくあるコンパイルエラーメッセージ(ライブラリ/ヘッダ系)

必要なライブラリを読み込んでいない場合

./Main.cpp:11:3: error: '[[cin]]' was not declared in this scope; did you mean  'std::cin'?
   11 |   cin >> n;
      |   ^~~
      |   std::cin
not declared(宣言されていない)と書かれている場合の別パターン。
先頭の#includeやusingを書き忘れているミスだが、エラーメッセージは未宣言の変数と同じもの。
using namespace std;をしない場合はstd::の付け忘れという場合もあるが、競プロでは常にusing namespace std;することを推奨。

よくあるコンパイルエラーメッセージ(その他)

全角文字を使った場合

./Main.cpp:18:3: error: extended character   is not valid in an identifier
   18 |    
      |   ^
extended character(拡張文字)というのが全角文字を意味する。
この例では^の位置に全角スペースが入っている。
消して、半角スペースやTABに変えること。

よくある警告

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

./Main.cpp:21:7: warning: unused variable 'a' [-Wunused-variable]
   21 |   int a = 0;
      |       ^
unused variable(未使用変数)と書かれている場合、宣言したのに使用していない変数がある。
ただコードを書きなおしたときに要らなくなったのを消し忘れただけなら、無視して問題ない。
しかし、これを使う何かの処理を書き忘れたという場合もあるので、念のため確認した方がいいかもしれない。
以下のように書くと、未使用でも警告が出ないようにすることはできる。
[[maybe_unused]] int a = 0;

符号が異なる2つの整数型で比較した場合

./Main.cpp:22:18: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<int>::size_type' {aka 'long unsigned int'} [-Wsign-compare]
   22 |   for (int i=0; i<a.size(); i++) {
      |  
comparison of integer expressions of different signedness(符号の異なる整数式の比較)と書かれている場合、符号あり整数と符号なし整数で比較を行っている。
上の例では、int型size_t型の間で比較を行っていて、.size()を使ったときにありがち。
特に問題がない場合も多いが、念のため本当に意図通りに動作するか確認しておいた方がいいかもしれない。
上の例の場合、iをsize_t型にしておけばこの警告は出ないが、しかし逆順ループでハマるリスクが生じることになる。
a.size()の代わりにssize(a)を使って符号付きの値に揃える方が無難か。

返り値があるべき関数でreturnし忘れた場合

./Main.cpp:6:1: warning: no return statement in function returning non-void [-Wreturn-type]
    6 | }
      | ^
no return statement(return文がない)と書かれている場合、自作の関数(void型でない)内にreturnを書き忘れている。
これが意図通りということはほぼないので、よくコードを見直して対応する。

実行時エラー RE

機械語に変換した後、実行中に何か実行を続けられなくなる問題が発生した場合のエラー。
原因がメモリ制限超過だった場合、REではなくMLE判定になる。

よくある実行時エラー

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

コードテストでの終了コードは139で、エラーメッセージは以下。
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※などにアクセスしようとしたが、その場所が範囲外だった。
2つの(which is 数)の、1つ目がアクセスしようとした場所、2つ目がそのvectorの実際の長さ。
上の例ではaというvectorの長さは5であり、アクセスできる場所は0から4まで。
その18446744073709551615番目にアクセスしようとしたのでエラーしている。
この(which is 数)はsize_t型で表示されるので、184で始まって615で終わる20桁の数は、実際には-1番目にアクセスしようとしたときに表示される。
機械語に直された後で発生しているため、コードテストでは元のコードのどの行で発生したのかわからない。
元の長さからどのvectorで発生したのか推測し、そのvectorを触っているところを地道に全て確認すること。
.at()ではなく[ ]を使った場合、このエラーは出ない。
ただしそれは、バグがないという意味ではなく、エラーしないままバグって動作するという意味である。

ゼロ除算をした場合

コードテストでの終了コードは136、エラーメッセージは出ない。
ゼロでの割り算は数学的に禁じられているはずなのだが、それが発生してしまった。
機械語に直された後で発生しているため、コードテストでは元のコードのどの行で発生したのかわからない。
コード中の割り算を地道に全て確認すること。
VSCode等の環境であれば、どの行で発生しているか確認することもできる。

メモリを使いすぎて落ちる場合

コードテストでの終了コードは139で、エラーメッセージは以下。
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
bad_allocと書かれている場合、新しい変数用のメモリ確保に失敗した。
超巨大なvectorを作った場合などに発生する。
データの持ち方を変えるなどして、もっとメモリが少なくて済む方法に変えるしかない。
なお、メモリより前に計算時間の方が足りなくなってTLEになることも多い。

論理エラー

実行はきちんと終了したが、処理の結果が意図通りになっていないエラー。
もしくは、各行では異常な処理は発生していないのだが無限ループで処理が終わらなくなってしまったエラー。
コンピューターは、書かれている通りしか動かない。
つまり、頭の中にあるコード通りに書けていないか、頭の中のコードがそもそも誤っている。
テストをきちんと実行することで、提出前に防いだり、WAの原因を突き止めたりする。

よくある論理エラー

無限ループが発生している(TLE)

forループwhileループのときに、終了条件を間違えるとループが終わらない場合がある。
プログラムが終了せず、コードテスト上では実行時間が>10000ms、提出時の判定ではTLEになる。
計算量的に問題がない確認が取れているのにこうなった場合、無限ループの可能性が高い。
ループ先頭にcerrなどで適当なメッセージを出力させてみると、エラー出力に無限にそれが出てくるという形で確認できる。
終了条件に用いている変数の挙動を確認してみると原因を探りやすい(ついでに上記の無限ループの確認もできる)。
そこで次項目以降に書いてある内容のどれかが起きている可能性が高い。

何かを書き忘れた

事前にソートし忘れた。
カウンターをインクリメントし忘れた。
結果を集計する変数に反映し忘れた。など。
小さいテストケースでcerrなどで変数の中身を出してみて、意図通りに変数の中身が変更されているかを確認する。
VSCodeなどで書いている場合は、ブレークポイントなどを用いてもっと簡単に中身を確認できる。

初期化がおかしかった

カウンターの0初期化をし忘れた。
最小値を探したいのに-1で初期化してしまった。
最小値を探すときに、適当に10^9で初期化したが、本当の答えがそれより大きかった、など。
上の「何かを書き忘れた」とまとめて同時に確認や対応できる。

使う変数を間違えた

vectorの長さとしてmを使うべきところnと書いてしまった。など。
入力変数に1つずつ最大や最小などの極端な値を与えてみると、露骨に変な挙動をして原因が見つかることが多い。
極端な値を入れる変数以外は、自分が正規の挙動を把握しやすい値がいいが、恒等元(和の場合の0など)はおかしな挙動をしても偶然正しい出力になる可能性が高いのでさすがに避ける。

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

==と書くべきところを=(代入)にした。
不等号の大小を逆にした。
不等号に等号をつけるかどうかを間違えた。
&&と||を間違えた、など。
不等号のミスを見つけるには、if分岐の条件についての境界値テストなどが有効。
論理式のミスには、複数の条件のうち1つだけ成り立つ/1つだけ成り立たないような入力を与えてみるのが有効。

0-indexと1-indexが意図せず混在している

プログラムは基本的に0番目始まりで動作するが、問題の数列は1番目始まりで書かれていることがほとんど。
結果、問題でk番目と言われて素直にプログラム上でk番目を見ると、見るべき場所が1つずれる。
見つけるには、位置情報の境界値テストなどが有効。
データの端にあるものを見る処理をさせると、.at()が範囲から1つはみ出る形でエラーし、それで気づける。
[ ]で書いている場合は、幸運にもそれで異常挙動してくれることに期待するしかないが。

小数の扱いに問題がある

本来等しいはずの2つの値が、丸めの関係でほんのわずかにずれて==でfalseと判定された。
割って切り捨てるとき、本来割り切れて3になるべきところ、丸めの関係で2.9999……の切り捨てで2になった。
10^15を超えるような大きな整数をdouble型で扱おうとして、一の位が狂った。など。
このように、プログラムは小数を扱うのが苦手で、思わぬ問題が発生することがある。
等しいかどうかは差が10^(-6)以下かどうかで判定する、切り捨てる前に10^(-6)を足す、long double型を使うなど応急処置の方法もあるが、それのせいで新しいバグになることもある。
できれば設計時点で可能な限り小数を避けて対応するようにしたい。
書いた後で不具合に気づいた場合、アルゴリズムを根本から変える必要がある場合もあり、他の論理エラーより修正が大変。

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

割って小数で答えるべき問題で、int型の除算をしてしまう、など。
また、整数の除算でも、C++は負数の除算の商や余りの仕様が通常の数学と異なるのにハマることも。
割り算する問題で負数や小数が絡むなら、きっちり確認しておきたい。

オーバーフローした

int型は21億程度までしか扱えない。
入力される値が10億程度の場合、それを3つ足すとアウト。
また、10^5程度の数同士を掛け算するのもアウト。
入力例は小さい数や短い数列で与えられがちなので、サンプルは通るが提出でWAが出た場合、これが原因である可能性が最も高い。
int型を一切使わず全部long long型にするという対応も可能ではあるが、long long型ですらはみ出る場合もあるわけで、本質的な対応にはなっていない。
テストよりは、計算に用いる数値が最も大きくなる入力例を手計算で考えてみるのがよい。

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

特殊な入力の場合だけif分岐で特殊な解答をしなくてはいけない場合、
ほとんどACして1ケース2ケースだけWAした場合、この疑いが強い。
境界値テストをしてみるのが有効。

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

「このアルゴリズムで解けるはず!」がそもそも間違っていることもある。
特に誤った貪欲法(アルゴリズム系)※を使ってしまうという形でやりがち。
愚直解で解くコードも作って、小さい入力のランダムテストで愚直解と比べるというのが有効だが、それ自体に時間がかかるのが難点。
最近更新されたスレッド
ウィキ募集バナー