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

#includeプリプロセッサ指令関連

Cのソースでおまじないのように必ず記述される#includeプリプロセッサ指令ですが、これの記述を間違えると外部のファイルが取り込まれないため、想定外のエラーがぞろぞろ出てくることがあります。

#記号を忘れた場合

リスト1は、前回紹介した九九プログラムのソースですが、先頭のincludeの前に#記号がありません(★マークの箇所)。このソースをコンパイルすると、以下のようなエラーメッセージが出力されます ※1

C:\CLANG\SRC\ex1201.c 1: include の関数定義がおかしい
C:\CLANG\SRC\ex1201.c 1: < のあたりに構文上の誤りがある
C:\CLANG\SRC\ex1201.c 4: パラメタ宣言はできない
C:\CLANG\SRC\ex1201.c 4: main はパラメタリストにない
C:\CLANG\SRC\ex1201.c 4: { のあたりに構文上の誤りがある
C:\CLANG\SRC\ex1201.c 7: 警告: 関数 printf は宣言されていないので、int 型とみなす
C:\CLANG\SRC\ex1201.c 14: 警告: main が未使用

リスト1:include指令の前に#記号がない~(ex1201.c)
include <stdio.h> -------- ★

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

メッセージの先頭に表示される“C:\CLANG\SRC\ex1201.c”の部分はソースファイルのパス(ファイルのありか)を示すもので、環境によって異なります。続く「1:」が行番号でその次に具体的なエラーの内容が示されます

1つのミスで7個のエラー!?

1箇所間違っただけなのに、7件のエラーが出力されました。メッセージを個別に見ていきましょう。

C:\CLANG\SRC\ex1201.c 1: include の関数定義がおかしい

明らかに★マークの箇所に対するエラーです。先頭に#記号がないため、コンパイラは“include”の行を『型の指定されていない関数の宣言』とみなして処理し、続く<stdio.h>の箇所でその定義が正しくないと判断します。

C:\CLANG\SRC\ex1201.c 1: < のあたりに構文上の誤りがある

上記のエラーに関連するエラーです。コンパイラが“include”を関数の宣言と見なしたため、当然それに続いて引数を示す()が記述されていると判断したものの、その代わりに“<”記号に出くわしてしまったためにこのエラーが発生します。

C:\CLANG\SRC\ex1201.c 4: パラメタ宣言はできない

4行目の“int main(void)”に対するエラーです。パラメタ(パラメータ)とは関数の引数のことです。これが宣言できないというのは、やはり最初のエラーに関連して、この行が“include”という関数宣言の続きと判断されているためです。

1行目(include...)の最後にCの構文の終わりを示す「;」記号がないため、この部分を1行目からの続きと解釈している訳です。

C:\CLANG\SRC\ex1201.c 4: main はパラメタリストにない

これも上のエラーと同様に、1行目のエラーに関連して“int main(void)”に対して発生したエラーです。“main”という語が“include”という関数の引数には存在しない……という意味です。

この段階で、最初のエラーに起因する「includeが宣言されていない関数の定義かもしれない」という判断がまだ続いていることが分かります。

C:\CLANG\SRC\ex1201.c 4: { のあたりに構文上の誤りがある

これも、1行目の“include”を宣言されていない関数の定義と見なしたために発生するエラーです。上記2つのエラーにあるとおり“int main(void)”が無効なのですから、続く処理ブロックの開始記号「{」も文法的に間違っていると判断されます。

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

これもまた、1行目の“include”に関連するエラーです。1行目が正しく処理されていないため、printfを含む標準関数の宣言を行っているstdio.hが取り込まれていません。よって、printf関数は宣言されてないことになります。

そのため、宣言されていないまま使用したprintf関数に対していわゆる「暗黙のint型」 ※2 が適用されるのです。

これは致命的なエラーではなく警告(warning)なので、コンパイルは中止されません。

C:\CLANG\SRC\ex1201.c 14: 警告: main が未使用

これもやはり、1行目のエラーに関連したものです。“int main(void)”というmain関数の定義行が“include”という関数定義の続きと見なされて無効になったため、Cプログラムの入り口であるmain関数が存在しないと判断されたのです。

これも警告であるため、コンパイルは中止されません。main関数を定義しないCのプログラムも存在するためです。 ※3

Cでは、型を宣言しないままに使用した関数や変数をとりあえずint型(最も効率がよいとされる整数型──サイズはOSや処理系によって異なる)と仮定してコンパイルを進めます
一般的なコンピュータのOSではmainが最初に実行される関数とされますが、それはあくまで標準的なスタートアップ・ルーチンの仕様に基づくプログラムの場合です。機器制御用のROMに焼かれるプログラムなどパソコンOSを使用しないシステムでは、main以外の関数が定義される場合もあります

全角スペースを入力した場合

日本語OSで開発していると、タブや半角スペースの代わりに全角のスペースを入力してしまうことがあります。

リスト2の★マークの箇所では、#include指令の前に2個の全角スペースが入力されています。#で始まるプリプロセッサ指令は、行の先頭から始まっていなければなりません。先頭とは、その前に他の予約語などがない状態のことで、タブコードや半角のスペースは無視されます。

しかし、全角のスペースはそもそもCのソースではコメントと""で囲んだ文字列定数(リテラル)以外には使われない文字なので、#の前だけではなくあらゆる場所でエラーを引き起こします。

リスト2をコンパイルすると、以下のように9件のエラーメッセージが出力されます。

C:\CLANG\SRC\ex1202.c 1: 不正な文字がある
C:\CLANG\SRC\ex1202.c 1: 不正な文字がある
C:\CLANG\SRC\ex1202.c 1: # のあたりに構文上の誤りがある
C:\CLANG\SRC\ex1202.c 1: include の関数定義がおかしい
C:\CLANG\SRC\ex1202.c 4: パラメタ宣言はできない
C:\CLANG\SRC\ex1202.c 4: main はパラメタリストにない
C:\CLANG\SRC\ex1202.c 4: { のあたりに構文上の誤りがある
C:\CLANG\SRC\ex1202.c 7: 警告: 関数 printf は宣言されていないので、int 型とみなす
C:\CLANG\SRC\ex1202.c 14: 警告: main が未使用

リスト2:全角スペースが挿入された~(ex1202.c)
  #include <stdio.h> -------- ★

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

全角スペースは不正な文字

では、メッセージを個別に見ていきましょう。

C:\CLANG\SRC\ex1202.c 1: 不正な文字がある
C:\CLANG\SRC\ex1202.c 1: 不正な文字がある

この2件は明らかに全角スペースに対するエラーメッセージです。2個の全角スペースに対して、1つずつエラーが発生しています。

C:\CLANG\SRC\ex1202.c 1: # のあたりに構文上の誤りがある

これも、上記のエラーに付随して発生したものです。#記号の前に半角スペースとタブ以外の文字(全角スペース)があるため、構文上の誤りとされたものです。

C:\CLANG\SRC\ex1202.c 1: include の関数定義がおかしい

先のリスト1の場合と同じで、プリプロセッサ指令を表す「#」記号が正しく認識されなかったため、“include”が宣言されていない関数の定義と認識されたためのエラーです。

C:\CLANG\SRC\ex1202.c 4: パラメタ宣言はできない
C:\CLANG\SRC\ex1202.c 4: main はパラメタリストにない
C:\CLANG\SRC\ex1202.c 4: { のあたりに構文上の誤りがある
C:\CLANG\SRC\ex1202.c 7: 警告: 関数 printf は宣言されていないので、int 型とみなす
C:\CLANG\SRC\ex1202.c 14: 警告: main が未使用

これらもリスト1の場合と同じで、#include指令が正しく認識されなかったために発生したエラーです。

コメントを入力した後など、かな漢字変換をOFFにし忘れて全角スペースを入力してしまうことは意外とあります。行の先頭をタブの代わりに半角スペースで字下げするスタイルを採っている人の場合、特に要注意です。

間違った全角スペースの入力を防ぐには、エディタで『タブやスペースを記号として表示する』ように設定しておくのがいいでしょう。

<>を忘れた場合

同じ#include指令でも、取り込むヘッダファイルを<>または""で囲むのを忘れるとどうなるでしょう?

その場合、以下のように1行のエラーメッセージが出力されます。

D:\Data\CLANG\SRC\ex1203.c 1: #include のファイル名がない

取り込むファイルは必ず<>または""で囲まなければならないため、そこで間違ってしまうと上記のようなエラーが発生してコンパイルは停止します。

上記2件の場合は#includeというプロプロセッサ指令が正しく認識されていなかったため、付随するエラーがたくさん表示されました。しかし#include指令は認識されたものの続くファイルが正しく指定されていない場合は、その段階で処理を続けることができなくなってコンパイルが停止されるため、付随するエラーは発生しません。

もちろん、実行ファイルは生成されません。

リスト3:ヘッダファイルを<>で囲むのを忘れた~(ex1203.c)
#include stdio.h -------- ★

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