{ }codelper

学ぶ › C

さらに先へ · 22

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

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

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

#include <stdbool.h>#include <stdio.h> /* C에는 null 정수가 없어서 성공 여부(bool)와 값(*out)을 따로 돌려줘요 */bool get(const int *a, int n, int i, int *out) {    if (i < 0 || i >= n) return false; /* 안 막으면 정의되지 않은 동작 */    *out = a[i];    return true;} int main(void) {    int n, q, x, y, v;    int a[100];    int sum = 0; /* = 0 을 빼면 쓰레기 값에서 시작해요 */    scanf("%d", &n);    if (n < 1 || n > 100) return 1; /* 배열 크기를 넘지 않게 */    for (int i = 0; i < n; i++) { /* i <= n 이면 한 칸 넘어가요 */        scanf("%d", &a[i]);        sum += a[i];    }    scanf("%d", &q);    for (int t = 0; t < q; t++) {        int i;        scanf("%d", &i);        if (get(a, n, i, &v)) printf("a[%d] = %d\n", i, v);        else printf("a[%d]: out of range\n", i);    }    scanf("%d %d", &x, &y);    if (y == 0) printf("%d / %d: cannot divide\n", x, y); /* 0으로 나누면 정의되지 않은 동작 */    else printf("%d / %d = %d\n", x, y, x / y);    printf("average %.1f\n", (double)sum / n); /* sum / n은 2 */    return 0;}
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 と書きます。

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

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

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

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

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

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

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

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

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

練習問題