わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

ヘッダファイルはコンパイルしないように

木曜の演習科目の授業のことです.こちらはいくつかのファイルを用意していて,makeによって,仕様どおりに動く実行ファイルができるよう,comm.hに書き加え,comm.cを新規に作成しなさい,という課題を与えています.
学生から相談がありました.どのようにcomm.hやcomm.cを修正しても,次のエラーメッセージが出るというのです(行番号は架空の値に置き換えました).

comm.c:100: error: conflicting types for ‘get_hostport’
comm.h:30: note: previous declaration of ‘get_hostport’ was here

このメッセージは…ヘッダファイルとCのファイルとで,それぞれ宣言している,get_hostport関数の引数・戻り値の型が一致しないという,ありがちなエラーです.
こういうときはgrepです.

grep -n get_hostport *.[ch]

を実行させて確認すれば,違いは明瞭…と思いきや,カッコ閉じのあとにセミコロンがあるかないかを除いて,完全に一致していました.
違和感を持ちつつも,次の基本チェックをしておきましょう.「どのようにソースを書き換えても」というときは,編集しているファイルと,コンパイル対象のファイルが違っていないか,チェックします.シェルの側は,ls -l*1pwdです.Emacsでは,編集しているバッファで,C-x C-fとすると,ミニバッファ(最下段)に,そのバッファのファイルのディレクトリ名が出ます.その後,C-g(必要なら連打)で止めます.ステータス行のファイル名にも注意を払って…しかしながら同一でした.
その次は…ソースファイルを少々(もちろん,すぐに戻せるように)書き換えてコンパイルしたとき,エラーメッセージがどう変わるか,見てみました.ですがこれでも,何が原因かよく分かりませんでした.
最後の手段は,学生のファイルを,私の手元で,いじってみることです.巡回指導はティーチングアシスタントの院生に任せ,ディレクトリをコピーして,悪戦苦闘していると,次のことが分かってきました.

  • comm.cに改行を入れたり空行を取り除いたりして,コンパイルすると,エラーメッセージの「comm.c:」の直後の行番号が変わる.
  • comm.hに改行を入れたり空行を取り除いたりして,コンパイルしても,エラーメッセージの「comm.h:」の直後の行番号は変わらない.
  • comm.cのget_hostportの関数名を変えると,エラーは解消される(がその後の工程でエラーになる---これは想定どおり).
  • comm.hのget_hostportの関数名を変えても,エラーメッセージは変わらない.

だいぶとcomm.cをいじりすぎたので,学生のファイルをコピーし直そう,と思って何気なくls -lを実行したときに,バイト数が異様に大きいファイルを発見しました.
comm.h.gchです.
「gch」で調べると…

$ gcc hoge.h

とコマンド実行すれば,hoge.h と同じディレクトリに hoge.h.gch というバイナリが作成される.その後,hoge.h をインクルードしたソースファイルをコンパイルすると,hoge.h の代わりに hoge.h.gch が使用されてコンパイル時間が短縮される.なお,.h.gch 使用時と未使用時のコンパイル済みオブジェクトは完全一致した.

GCC ヘッダファイルのプリコンパイル - reroの日記

びっくりです.そしてcomm.h.gchを削除してからmakeで…エラーがなくなりました.
時系列としては,この学生さん,何かのときにcc comm.hとやってしまい,その後comm.hとcomm.cを書き換えていったのでしょう.comm.cの中に「#include "comm.h"」と書いてあっても,コンパイル時に参照されているのは,comm.h.gchだったという次第です.
問い合わせのあった学生のところへ行き,このファイルを削除するよう,指示をしました.受講生全体には,ヘッダファイルをコンパイルしないよう,アナウンスしました.

*1:1ファイル1行で情報が出るほか,ファイルの最終更新日時も出るので,他と比べて古いファイルを見つけることができます.