他の言語では外部ファイルも同じ拡張子のソースコードでそれを読みこむのがありますよね。
C言語で拡張子が違くなっているのは何故ですか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
回答6件
0
まずC言語と他の言語を比較してみる。すると最近の他の言語にはモジュールという物があり(詳細割愛)、ほかのモジュールを読み込むための構文が用意されている。モジュールの場合、外部公開するか否かを指定する(通常exportとかexternとかそんなキーワードを使う)ことで、他のモジュールからそのモジュールが読み込めるようになる。
C(そしてC++)ではどうかというとそんな高尚なものは存在しない。あるのは#includeというプリプロセッサマクロで、こいつはただの自動コピペマシーンだ。指定したファイルの中身をそっくりそこにコピペする。
すると困ったことがあって、複数の翻訳単位からincludeされることを想定していないファイルを読み込んでしまうと定義が複数出来上がりリンカーで衝突する。これはまずい。
解決策は
- 宣言のみを集めたファイルを作ってそれをincludeする
inlineキーワードを利用して定義が複数出来上がっても同一のものなら衝突しないよう指示する
となる。
ここでヘッダーファイルとは宣言のみを集めたファイルを一般に指す。定義がないことをわかりやすくするため、.hとか.hppとかいう拡張子を利用することが多い。
2の手法は昔のC(C89)だと標準規格では用意されていなかったため、いまでもC言語界隈ではマイナーな解決策だ。しかしC++界隈ではtemplateの存在や名前マングルの問題からヘッダーオンリーライブラリと呼ばれる、2の手法を利用してヘッダーに実装をすべて書いてあるライブラリが流行している。
で、結局ヘッダーファイルとは何だったのか?歴史的変遷ののち、現在では、#includeで読み込まれることを想定しているファイル、となる。そしてこれらに対して.hとか.hppとかいう拡張子を利用することが常識である。
投稿2018/07/13 08:39
編集2018/07/13 08:41総合スコア5852
0
拡張子が別れているのは、両者ともC言語のソースコードではあるものの、使い方が違うからです。コンパイラやmakeなどは拡張子の使い分けを前提に動いていますので、違う拡張子にするのであれば手動設定する必要が出てきます。
.cファイル…関数や変数の定義を行う。基本的に同じ定義が重複するとリンクできないので、.cファイルを#include対象とすることは通常ない。.hファイル…関数や変数、型の宣言を行う。呼ぶ各箇所で宣言が必要になるので、#includeを使って取り込む(なお、コンパイル前に処理されるマクロや、暗黙を含めinlineな関数、テンプレートなどについては、ヘッダで定義まで行って、そのまま複数ファイルで#includeしても問題ありません)。
他の言語での事例ですが、JavaやC#はコンパイル後のファイル自体が型情報を持っているためそのままコンパイラで処理ができ、ヘッダーは不要です。一方、C言語の場合は「型情報はソースコード内から読み取る」ということになっているので、ソースコードに#includeする形のヘッダーが必要になります。
また、PerlやPHPなどのスクリプト言語は型定義と実装を分けませんので、ヘッダーファイルという概念は存在しません。
投稿2018/07/13 08:15
編集2018/07/13 08:27総合スコア147054
0
大体、他の人の書かれた通りなんですが、C言語の場合、歴史を無視できないじゃないかと。
確かに関数の宣言は大切ではありますが、初期の Cでは、宣言無しで使われると引数不明、戻り値 int とみなされていた。それよりもそれぞのソースファイルで共通に参照される 定数とか、別個に定義すると間違えるので、共通ファイルに置かれたじゃないですか?
今時のシステムでは、沢山のファイルを参照してなんて容易ですが、C言語は1990年よりも前だったかと。そして当時でも最新とは言えないシステムで作られたので、他のファイルを参照するにしても指定したファイルのみにした方が、効率が良い、、そんな発想ではないかと思います。(アセンブラ...)
それを今でも引きずっているのが、C言語でしょう。その系譜にありながら、使い勝手向上したのが、Java とか、C#
拡張子については他の人も書かれているように、共通化しておけば、分かり易く使い良い。(特に make ファイルで参照する時に、.c.o なんて定義すれば、かなり省略した書き方ができる)
投稿2018/07/13 13:57
総合スコア6385
0
C 言語では、「関数の使用は宣言の後でなくてはならない」という制限があります。
関数 func() を使う前に、func() の定義自体を行って宣言しておかなくてはなりません。
※main() は例外です
しかしこれを常に行うのは不可能(ライブラリ関数はソースがありませんからね)なので、前方宣言という機能があります。
関数の型(呼び出しパラメータの数と型、戻り値の型)だけを先に定義して、実装は後回しにするという方法です。これだと、実装の位置に関わらず宣言さえ正しければうまくコンパイルとリンクが行われます。ライブラリ関数の場合はコンパイルがなく、リンクの時点で実装がうまく結合されます。
この宣言のために使われるのが、ヘッダファイル(*.h)です。
前方宣言がなくてもコンパイル・リンクができるような言語もあります。逆に前方宣言を禁止している(必ず実態が利用の前に必要)ことで、コンパイル速度を稼ぐ言語もあります。
投稿2018/07/13 09:05
総合スコア13707
0
こんにちは。
物事を分かりやすくするための習慣です。
複数の .c ファイルから#includeされるファイルには、 .h との拡張子を付ける習慣です。
これにより、そのソースを読む人は理解が捗ります。
ですので、守る必要はないですが、理由もなく守らないとそのソースを見る人を無駄に混乱させるため嫌がられます。
投稿2018/07/13 07:37
総合スコア23274
退会済みユーザー
2018/07/13 07:57
退会済みユーザー
2018/07/13 08:18
退会済みユーザー
2018/07/13 08:19
2018/07/13 11:11
0
違いは無いと言えば無いですね。
.hや.c以外の拡張子でも、中身がテキスト形式でC言語の構文で書かれていればコンパイルは可能です。
実際、過去に上記以外の拡張子のソースコードが含まれているプロジェクトで仕事したこともあります。
ただ、コンパイル対象のファイルをMakeFile等に列挙する際に ファイル名.拡張子 とひとつずつ書くよりも
「このフォルダの下の.c全部」と書いた方が楽なので普通は拡張子は揃えます。
ちなみにですが、普通は.hのファイルはコンパイル対象ではありません。
C言語ではxxxx.cというファイルをコンパイルしてxxxx.objのようなオブジェクトファイルを生成し、最後にそれらオブジェクトファイルと必要なライブラリファイルをリンクして実行ファイルを生成します。
先日知人がVisualStudioでxxxx.cからxxxx.hにリネームした後で再度xxxx.cを新規作成した際、xxxx.hがコンパイル対象になってしまいトラブっていました。
(xxxx.hとxxxx.cの両方がxxxx.objの生成元となってしまい、後勝ちで上書かれてしまっていた)
投稿2018/07/13 07:41
編集2018/07/13 07:42総合スコア62
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。