第12回
エラーメッセージと対処方法(2)~1つのミスとたくさんのエラー

関数名と予約語関連

関数名やその他の予約語を綴り間違えることもよくあります。関数は宣言と定義を行ってから使用するのが本来の姿ですから、綴りを間違うと『宣言も定義もされていない関数』と認識されます。ソース中で定義されていなくてもどこかに実体があるものとして処理が続けられるため、警告が発せられるにとどまります。

関数名を綴り間違えた場合

リスト4の★マークの箇所では、本来は“printf”とすべきところを間違って“pirntf”としています。そのような関数あるいは予約語は存在しません。すると、以下のような警告が発生します。

C:\CLANG\SRC\ex1204.c 7: 警告: 関数 pirntf は宣言されていないので、int 型とみなす

関数名を間違えると、それは『宣言されていない関数』と見なされます。宣言されていない関数はいわゆる『暗黙のint型』ルールが適用されてint型の戻り値を持つものとして処理されます。

つまり、この例では“pirntf”というint型の関数が存在するものとしてソースがコンパイルされ、objファイルが生成されます。

リスト4:関数名を綴り間違えた~(ex1204.c)
#include <stdio.h>

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

リンクの段階でエラーになる

これまでに紹介したエラーではそれ以上コンパイルを続けることができないため、コンパイルが中止されてobjファイルが生成されませんでした。しかし関数が宣言されていないだけの状態では、とりあえずそれをint型としてコンパイルを続け、objファイルが生成されます。

そして、コンパイル後に生成されたobjファイルをリンクする時点で、先にint型として処理した『存在しない関数』(例では“pirntf”)の実体がどこにも見つからないことが判明するため、続けて以下のようなリンカのエラーメッセージが表示されます。

C:\CLANG\SRC\ex1204.obj(C:\CLANG\SRC\ex1204.C): Undefined symbol: pirntf_

“Undefined symbol”とは「識別子が見つからない」という意味です。コンパイル時には警告で済むのですが最終的にリンクの段階でエラーとなる訳です。

但し、リンクでエラーが発生してもそれまでの処理結果によって実行ファイル(拡張子.exe)が生成されます。もちろん実行しても、存在しない関数を呼び出すところでエラーとなって正常に動作しません。例に示したソースからはex1204.exeが出来上がりますが、実行すると何の反応も示さず「うんともすんとも状態」となって、[Ctrl]+[C}で強制終了しなければならなくなります。

暗黙のint型という落とし穴

関数と同じように、変数も宣言しないで使うと暗黙のint型が適用されるのが本来のCの仕様ですが、前回紹介したようにLSI-C 86では変数が宣言されていない場合はその時点(宣言されていない変数を使用したソースをコンパイルした時点)で『変数が宣言されていない』というエラーが発生します。

一方関数の場合は致命的なエラーとはならず警告として処理され、コンパイルが続けられます。

変数に対する『暗黙のint型』の適用とエラーか警告かの判断は処理系によって異なります。昔の処理系では、警告で済ませてそのままコンパイルを続けてしまうものが多かったように記憶しています。

予約語の綴りを間違えた場合

関数ではなくforやifなどの予約語(Cの処理系に組み込まれている基本的な命令語)を綴り間違えると、単純な構文上の誤り(sintax error)となります。

リスト5では、★マークの箇所で反復命令の“for”を間違えて“foe”としています。するとコンパイル時に、以下のようなエラーが発生します。

C:\CLANG\SRC\ex1205.c 6: ; のあたりに構文上の誤りがある
C:\CLANG\SRC\ex1205.c 6: { のあたりに構文上の誤りがある

もうお分かりかと思いますが、Cコンパイラは予約語の綴り間違いを発見した後もそれに基づいてとりあえず「;」で行が終わるまでを1つの構文として処理を続けます。

そのため、間違いの根源である“foe”の箇所ではなく、その行の終わりである「;」記号やその行に続く処理単位の始まりである「{」記号の箇所までたどり着いたときに、はじめてそれが構文上の誤り(文法上のミス)であると認識して上記のような『行番号のズレた』エラーを発生するのです。

リスト5:予約語を綴り間違えた~(ex1205.c)
#include <stdio.h>

int	main(void)
{
  int i, j;
	foe (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);
}

あとがき


ソース入力での綴り間違いと、その結果生じるエラーを紹介してきました。

1つの間違いによって発生したエラーが、次のエラー、そのまた次のエラーを呼んで──という具合に、芋づる式にエラーが発生することがお分かりいただけたと思います。

実際、発端となった1箇所のミス(サンプルでは★マークの行)を修正して再コンパイルすれば、それに付随して発生した数行のエラーメッセージも嘘のように消えてしまいます。

なお、原因が同じならコンパイル・エラーの内容はどの処理系でも共通していますが、出力されるエラーメッセージの内容や表示の仕方は、処理系によって異なります。重要なことは、根源のエラーを見つけてそれを修正することです。付随するたくさんのエラーメッセージに惑わされないようにしましょう。

基本は以下の2点です。

1.最初のエラーメッセージに注目する
2.エラーメッセージで指摘された箇所の前後を調べる

【注意】
今回提供するサンプルのCソースはエラーメッセージを表示させることが目的のため、綴りなどの間違いが入っています。従って、コンパイルしても正常なプログラムは生成されません。

拡張子.ERRのファイルはエラーメッセージを保存したテキストファイルです。これは参考のために提供するものであり、本文にも書いたように、処理系が異なればエラーメッセージも異なるため、必ずしもこれらと同じメッセージが出力される訳ではありません。


Downloadサンプルファイル (LZH形式 1.99KB)