さらに先へ · 22
よくある間違いとエラーメッセージ
エラーメッセージの読み方と、どの言語でも出る間違い:1 つずれ、= と ==、整数の割り算、null、範囲外の添字。
どの言語でも出る間違いがあります。
エラーメッセージの読み方と一緒に覚えましょう。
下の例は、添字が範囲内か、割る数が 0 でないかを先に確かめてから使います。
def get(a, i): if 0 <= i < len(a): # a[-1]도 막아요(Python에서는 마지막 값) return a[i] return None def safe_div(x, y): if y == 0: return None return x // y n = int(input())a = list(map(int, input().split()))input()for i in map(int, input().split()): v = get(a, i) if v is None: # if not v: 로 쓰면 0도 걸려요 print(f"a[{i}]: out of range") else: print(f"a[{i}] = {v}")x, y = map(int, input().split())q = safe_div(x, y)print(f"{x} / {y}: cannot divide" if q is None else f"{x} / {y} = {q}")print(f"average {sum(a) / n:.1f}")const tok = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let p = 0;const n = tok[p++];const a = tok.slice(p, (p += n));const q = tok[p++];const idx = tok.slice(p, (p += q));const [x, y] = tok.slice(p, p + 2); // a[7]은 오류 없이 undefined라서 직접 검사해요const get = (arr, i) => (i >= 0 && i < arr.length ? arr[i] : null);const safeDiv = (u, v) => (v === 0 ? null : Math.trunc(u / v)); // u / 0은 Infinity for (const i of idx) { const v = get(a, i); console.log(v === null ? `a[${i}]: out of range` : `a[${i}] = ${v}`);}const d = safeDiv(x, y);console.log(d === null ? `${x} / ${y}: cannot divide` : `${x} / ${y} = ${d}`);let sum = 0;for (let i = 0; i < n; i++) sum += a[i]; // <= n 이면 undefined를 더해 NaNconsole.log(`average ${(sum / n).toFixed(1)}`);import { readFileSync } from "node:fs";const tok: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let p = 0;const n = tok[p++];const a = tok.slice(p, (p += n));const q = tok[p++];const idx = tok.slice(p, (p += q));const [x, y] = tok.slice(p, p + 2); // a[7]은 오류 없이 undefined라서 직접 검사해요const get = (arr: number[], i: number): number | null => (i >= 0 && i < arr.length ? arr[i] : null);const safeDiv = (u: number, v: number): number | null => (v === 0 ? null : Math.trunc(u / v)); for (const i of idx) { const v = get(a, i); console.log(v === null ? `a[${i}]: out of range` : `a[${i}] = ${v}`);}const d = safeDiv(x, y);console.log(d === null ? `${x} / ${y}: cannot divide` : `${x} / ${y} = ${d}`);let sum = 0;for (let i = 0; i < n; i++) sum += a[i]; // <= n 이면 undefined를 더해 NaNconsole.log(`average ${(sum / n).toFixed(1)}`);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 }}using System;using System.Linq; class Program{ // int?는 '수 또는 null'이에요 static int? Get(int[] a, int i) => i >= 0 && i < a.Length ? a[i] : null; // 안 막으면 IndexOutOfRangeException static int? SafeDiv(int x, int y) => y == 0 ? null : x / y; // 안 막으면 DivideByZeroException static void Main() { int n = int.Parse(Console.ReadLine()); int[] a = Console.ReadLine().Split(' ').Select(int.Parse).ToArray(); Console.ReadLine(); foreach (int i in Console.ReadLine().Split(' ').Select(int.Parse)) { int? v = Get(a, i); Console.WriteLine(v is null ? $"a[{i}]: out of range" : $"a[{i}] = {v}"); } int[] xy = Console.ReadLine().Split(' ').Select(int.Parse).ToArray(); int? d = SafeDiv(xy[0], xy[1]); Console.WriteLine($"{xy[0]} / {xy[1]}" + (d is null ? ": cannot divide" : $" = {d}")); Console.WriteLine($"average {((double)a.Sum() / n).ToString("F1")}"); // a.Sum() / n은 2 }}#include <cstdio>#include <iostream>#include <optional>#include <vector> // optional<int>는 '수 또는 없음'이에요std::optional<int> get(const std::vector<int>& a, int i) { if (i < 0 || i >= (int)a.size()) return std::nullopt; // a[i]는 검사 안 함, a.at(i)는 예외 return a[i];} std::optional<int> safeDiv(int x, int y) { if (y == 0) return std::nullopt; // 0으로 나누면 정의되지 않은 동작 return x / y;} int main() { int n, q, x, y; std::cin >> n; std::vector<int> a(n); long long sum = 0; // = 0 을 빼면 쓰레기 값에서 시작해요 for (int& v : a) std::cin >> v, sum += v; std::cin >> q; for (int t = 0; t < q; t++) { int i; std::cin >> i; if (auto v = get(a, i)) std::cout << "a[" << i << "] = " << *v << '\n'; else std::cout << "a[" << i << "]: out of range\n"; } std::cin >> x >> y; if (auto d = safeDiv(x, y)) std::cout << x << " / " << y << " = " << *d << '\n'; else std::cout << x << " / " << y << ": cannot divide\n"; std::printf("average %.1f\n", (double)sum / n); // sum / n은 2}#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;}get(a, i){ もし (i >= 0 && i < a.個数) { 返す a[i]; } 返す なし;} n = コンソール.行読み().数に();a = コンソール.数読み();q = コンソール.行読み().数に();それぞれ (i の中 コンソール.数読み()){ v = get(a, i); もし (v == なし) { コンソール.書く("a[" + i + "]: out of range"); } でなければ { コンソール.書く("a[" + i + "] = " + v); }}xy = コンソール.数読み();もし (xy[1] == 0){ コンソール.書く(xy[0] + " / " + xy[1] + ": cannot divide");}でなければ{ コンソール.書く(xy[0] + " / " + xy[1] + " = " + 数学.商(xy[0], xy[1]));}합 = 0;繰り返す (k = 0; k < a.個数; k++){ 합 += a[k];}コンソール.書く("average " + 文字.桁そろえ(합 / n, 1));5 3 1 4 1 5 3 2 7 -1 7 0
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 は真) |
| TypeScript | JavaScript と同じです |
| Java | 1 行目が 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 の
エラーで止まるのは、むしろありがたいことです。
本当に怖いのは、C・C++ の範囲外アクセスや JavaScript の
undefined のようにエラーなしで間違った値が出る場合です。