{ }codelper

学ぶ › Java

さらに先へ · 22

よくある間違いとエラーメッセージ

エラーメッセージの読み方と、どの言語でも出る間違い:1 つずれ、= と ==、整数の割り算、null、範囲外の添字。

どの言語でも出る間違いがあります。
エラーメッセージの読み方と一緒に覚えましょう。
下の例は、添字が範囲内か、割る数が 0 でないかを先に確かめてから使います。

import java.util.Scanner; public class Main {    // int에는 null을 못 담아서 Integer로 돌려줘요    static Integer get(int[] a, int i) {        if (i >= 0 && i < a.length) return a[i]; // 안 막으면 ArrayIndexOutOfBoundsException        return null;    }     static Integer safeDiv(int x, int y) {        if (y == 0) return null; // 안 막으면 ArithmeticException        return x / y;    }     public static void main(String[] args) {        Scanner in = new Scanner(System.in);        int n = in.nextInt();        int[] a = new int[n];        int sum = 0;        for (int i = 0; i < n; i++) { // i <= n 이면 한 칸 넘어가요            a[i] = in.nextInt();            sum += a[i];        }        int q = in.nextInt();        for (int t = 0; t < q; t++) {            int i = in.nextInt();            Integer v = get(a, i);            System.out.println(v == null ? "a[" + i + "]: out of range" : "a[" + i + "] = " + v);        }        int x = in.nextInt(), y = in.nextInt();        Integer d = safeDiv(x, y);        System.out.println(x + " / " + y + (d == null ? ": cannot divide" : " = " + d));        System.out.println(String.format("average %.1f", (double) sum / n)); // sum / n은 2    }}
stdin
5
3 1 4 1 5
3
2 7 -1
7 0
stdout
a[2] = 4
a[7]: out of range
a[-1]: out of range
7 / 0: cannot divide
average 2.8

ポイント

  • エラーメッセージではエラー名・理由・行番号を探します。
    Python は一番下の行から読みます。
  • コンパイルする言語(C・C++・Java・C#)では、エラーがいくつ出ても最初のエラーから直します。
    後ろのものは最初のエラーにつられて出ていることが多いからです。
  • 添字は 0 から n - 1 までなので、ループは i < n です。
    i <= n と書くと 1 つはみ出す 1 つずれ(off-by-one)になります。
  • = は代入、== は比較です。
  • 割る前に 0 でないか、使う前に空の値(null)でないかを先に確かめます。
  • 変数は作るときに初期値を与えます(int sum = 0;)。

言語ごとの書き方

Python最後の行に IndexError: list index out of range のような名前と理由、その上に行番号。
0 で割ると ZeroDivisionError、None で計算すると TypeError、ない名前は NameError。
a[-1] はエラーではなく最後の値で、if not v: は 0 も引っかけます(v is None で確かめます)
JavaScript範囲外の添字はエラーなしで undefined、7 / 0 は Infinity。
空の値は null と undefined で、undefined + 1 は NaN、null + 1 は 1 になって黙って間違えます。
比較は ===("1" == 1 は真)
TypeScriptJavaScript と同じです
Java1 行目が ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 5、下の at Main.main(Main.java:12) が行番号。
0 で割ると ArithmeticException、null は NullPointerException(int には入らないので Integer)。
初期値がないと "might not have been initialized" のコンパイルエラー
C#範囲外は IndexOutOfRangeException、0 で割ると DivideByZeroException。
空の値は int?。
初期値がないとコンパイルエラー CS0165
C++a[i] は検査しないので範囲外は未定義動作、a.at(i) は std::out_of_range 例外。
整数の 0 での割り算も未定義動作(たいてい落ちます)。
空の値は std::optional。
int sum; はごみの値から始まります
C配列の範囲検査がないので未定義動作:でたらめな値、別の変数の書き換え、落ちる。
0 での割り算と初期化していないローカル変数は C++ と同じです
Craykon範囲外は「リストには 0〜4 番しかありません」で止まります。
7 / 0 は Infinity、数学.商(7, 0) は「0 で割れません」。
なし + 1 は「なしでは計算できません」(文字列とつなぐと 빈값 と表示)、ない名前は「まだ作られていません」

よくある間違い

i <= n で 1 つはみ出したC · C++ · Java · C# · JavaScript · TypeScript

int sum = 0;
for (int i = 0; i <= n; i++)
    sum += a[i];

→ Java・C# は範囲外の例外、JavaScript は undefined を足して NaN、C は黙ってごみの値を足します

i < n と書きます。

平均が整数になったJava · C# · C · C++

int sum = 7, n = 2;
double avg = sum / n;

→ avg は 3.0 です。7 / 2 が整数の割り算なので、小数が先に捨てられます

片方を浮動小数点数にします:(double)sum / n。

== の代わりに = を書いたC · C++ · JavaScript

if (x = 5) {
    puts("five");
}

→ エラーなしで x に 5 が入り、いつも真になります(gcc -Wall なら警告してくれます)

比較は ==。
Java・C# はコンパイルエラー、Python は SyntaxError(3.10 からは "Maybe you meant '=='")、Craykon は文法エラーで止めてくれます。

言語ごとの違い
エラーで止まるのは、むしろありがたいことです。
本当に怖いのは、C・C++ の範囲外アクセスや JavaScript の undefined のようにエラーなしで間違った値が出る場合です。

練習問題