第11回
エラーメッセージと対処方法(1)〜エラーの原因を突き止める

宣言を忘れた場合のエラー

Cでは、1つのミスが次々とエラーを引き起こし、エラーメッセージの洪水となる場合があります。これも間違ったソースで試してみましょう。

エラーの洪水?!

リスト4では、最初に行うべきカウンタ変数iとjの宣言を忘れています。このソースをコンパイルすると、リスト5のように大量のエラーメッセージが出力されます。

記述を忘れたソースはたった1行ですが、エラーメッセージは「変数の宣言を忘れた」ことではなく、変数iとjを使っているすべての箇所に対して「iが定義されていない」「jが定義されていない」という形で、個別の問題を指摘する内容になっています。

リスト4:変数の宣言を忘れたソース(ex1103.c)
#include <stdio.h>

int	main(void)
{
	★ ---- 変数宣言がない

	for (i = 1; i <= 9; i++) {
	  printf("◆ %d の段\n", i);
	  for (j=1; j<=9; j++) {
		printf("%d x %d = %d\n", i, j, i * j);
	  }
	  printf("----------------\n", i);
	}
	return (0);
}

リスト5:リスト4のソースをコンパイルしたときのエラーメッセージ
C:\CLANG>LCC -oC:\Clang\Exe\ex1102.exe C:\Clang\Src\ex1102.c
C:\Clang\Src\ex1102.c 6: 'i' undefined
C:\Clang\Src\ex1102.c 6: 'i' undefined
C:\Clang\Src\ex1102.c 6: 'i' undefined
C:\Clang\Src\ex1102.c 7: 'i' undefined
C:\Clang\Src\ex1102.c 8: 'j' undefined
C:\Clang\Src\ex1102.c 8: 'j' undefined
C:\Clang\Src\ex1102.c 8: 'j' undefined
C:\Clang\Src\ex1102.c 9: 'i' undefined
C:\Clang\Src\ex1102.c 9: 'j' undefined
C:\Clang\Src\ex1102.c 9: 'i' undefined
C:\Clang\Src\ex1102.c 9: 'j' undefined
C:\Clang\Src\ex1102.c 11: 'i' undefined

示された箇所の直前を疑う

ここでも、Cコンパイラのエラーメッセージは直接間違いのあった箇所を指摘するのではなく、間違いが原因で正しくコンパイルできなかった箇所を指摘しています。

リスト4の場合、変数が宣言されるべき箇所に変数宣言が記述されていないことは、コンパイルの段階では判明しません。そして、変数が使用された時点で「その変数が宣言されていない」ことが明らかになるため、そこではじめてエラーと判断されるのです ※5

この例では、メッセージを読めば変数が宣言されていないことがすぐに分かりますが、場合によってはどこに原因があるのか判断が難しいこともあるでしょう。そのような場合、最初のエラーメッセージで示された箇所の直前をまず疑ってみましょう。

Cでは変数宣言をブロックの先頭で行わなければならないため、変数がソースの後ろの方で使われる場合には、宣言忘れのミスも後ろの方で指摘されます。この点にも注意しておきましょう。

変数を宣言するということは、変数の名前と型を定義することと同義です

Visual C++の場合

Visual C++ 2005で変数宣言を忘れた場合は、以下のように最初に問題となる箇所だけが指摘されます。
エラー	1	error C2065: 'i' : 定義されていない識別子です。
エラー	2	error C2065: 'j' : 定義されていない識別子です。
同じエラーがいくつも表示されるより、こちらの方が初心者には親切です。ただ、UNIX系OSのGCCを含めて、一般的なCのコンパイラはLSI-C同様にぶっきらぼうなメッセージを出力します。

なお、本来Cでは宣言されていない変数が使われた場合、自動的にそれをint型として処理するいわゆる『暗黙のint型』というお約束があります。そしてそのような場合には、エラーではなく警告(warning)メッセージが出力されるのが標準的でした。

従って、かつてはこのような変数宣言のミスは、警告として処理されていました。しかし、現在では未定義の識別子(変数や関数の名前)に対して厳密にエラーと判断するようになっています。

警告とエラー

警告も一般的なエラーも『ソースが規則通りに書かれていない』点を指摘するのは同じですが、両者には以下のような違いがあります。
警告:コンパイルを進めるのに支障はない。
エラー:これ以上コンパイルを進められない。
後者の一般的なエラーを「致命的エラー」と呼ぶこともあります。致命的とは大げさですが、コンパイラが処理を中止し、オブジェクトファイルが生成できない――という意味です。

この他にリンク時に発生するリンクエラーがありますが、これについては本コラムの第3回で説明したとおりです。

あとがき

hiropの『ちょっと気になる専門用語』〜致命的……って、なんか物騒

致命的エラー

本文で『致命的エラー』という言葉を使いました。コンパイルを進められない=これ以上処理を続行できない、という意味なのですが、なんだか大げさな気がします。これは、英語では“fatal error”と表記されます。fatalは「宿命の、取り返しのつかない、重大な」といった意味の形容詞で、確かに日本語では『致命的な』と訳すことが可能です。

しかし、英語と日本語は直接に意味を置き換えられるほどに同じではありません。日本語で「致命的」と言った場合、コンパイルという処理だけではなく、コンパイラそのものやコンパイルしているソースそのものが壊れてしまうような、深刻なイメージを喚起してしまいます。

実際のところ、ニュアンスとしては「どうしようもない」といった程度の意味なので、『続行不能なエラー』とでも訳せば分かりやすかったように思います。

メモリが壊れる?!

その他、アセンブリ言語の解説書などでは『レジスタが破壊される』といった表現にお目にかかることがあります。はじめてこの表現を目にした人は、「え゛ーーーっ、レジスタが壊れたらCPUも壊れちゃうよ!」と驚くかもしれません。

もちろん、これは『レジスタの保持する値が壊されてゼロまたは不定な値となる』ことを意味しており、レジスタが物理的に壊れることではありません。

この表現も、英語では“destroyed”と表記されたもので、日本語では一律に『破壊される』と訳されますが、ニュアンスとしては『無効にされる』といった程度です。

同じような意味の英語に“broken”がありますが、こちらは『連綿と続いていた処理が途中で終了させられる』といったニュアンスです。

杓子定規な和訳のせいかも?

この他にも、プログラミングで用いられる日本語には、物騒な表現やとんでもない表現がいくつか見られます。慣れた人には何でもないことなのですが、普通の日本語に親しんでいる一般人はどきっとしてしまいます。

致命的とか破壊とかいった怖い表現ではなくても、例えば“communication”はIT技術では『通信』と訳されますが、一般的には『意思疎通』と訳されます。実際には「連絡・伝達」というニュアンスの言葉です。

他国語を和訳する場合、辞書的に杓子定規に日本語に置き換えればいいというものではありません。このことは、一般的な書物の翻訳をしていれば当たり前なのですが、コンピュータ技術に関する英文の和訳では、一般的な日本語のニュアンスを意識しないまま日本語に置き換えられることが多かったようです。

翻訳の専門家ではなく、技術者が先に和訳したからでしょう。言ってみれば、翻訳者のデリカシーの問題だったのだと思います。