こんにちは、ナナです。
プリプロセッサ3大機能のうちの1つ「include」機能について解説しましょう。
「include」機能を使用することで、他の人が作ったプログラムを利用することができ、皆さんが作ったプログラムを他の人に利用してもらうこともできるようになります。
「include」機能を理解するためには、最低限「関数」に関しての知識は得ておきましょう。これが理解する上での最低ラインの知識です。
本記事では次の疑問点を解消する内容となっています。
では、インクルード機能の使い方を学んでいきましょう。
プリプロセッサが何かわからない方は、こちらの記事を先に見ておくとよいでしょう。
include は「おまじない」…はもう卒業しよう
師匠!すごく今更で恐縮なんですが、プログラムの最初に書く「include」って何なんですか?
とりあえず毎回書いていますが、何のために書いているのか知りたいのですっ!
うん、そろそろ教えてもいい頃だね。「include」とはね、コピペ職人さんだよ。今日はスペシャルゲストとしてお招きしておいたよ!
おれっちだぜ。職人芸を見せてやるぜぃ!
インクルードと言えば「Hello World」を表示する時にお世話になる「おまじない」ですね。
#include <stdio.h> // おまじない!
int main(void)
{
printf("Hello World!");
return 0;
}
多くの書籍やサイトでは、最初に作るプログラミングとして「Hello World」を表示させます。その時「おまじないだから、最初にincludeと書いて!」とされるやつです。
しかし、これは致し方ないのです。includeを理解するための知識の下準備が整っていない状態では説明のしようがないのです。
でも、もう大丈夫です。カリキュラムを順に実施してきた方であれば、理解するための準備が整っています。
includeとは「コピペ職人」である。その真意を解説していきましょう。コピペは皆さん知ってますよね。
他の文章や画像から必要部分の写しを取り、別の場所に貼り付けること。
includeの意味と役割
コピペ職人!さっそく質問ですが「include」とは何なのか?それを教えてください。
よし、じゃあまずは「include」がどんな風に使われているかを観察してから語ってやるっちよ。普段通りに書いてみせるぜ、Yo。
includeの書き方の基礎
みなさんはここまでプログラムの先頭に「include」を次のように書いてきましたね。
#include <stdio.h>
#include <string.h>
includeキーワードの後ろに<ファイル名>と書かれています。このように、「#include」の後ろには必ずファイル名を付与する必要があります。
#include <ファイル名>
インクルードとは結局何なのか?
includeとは和訳すると「含む」です。実はこの「含む」という言葉は「コピー&ペーストする」という処理を表現したものです。
つまり、includeとは
指定したファイル内容をその箇所にコピー&ペーストする
という機能なのです。
おれっち=include、それすなわち、include=おれっちってことだぜっ、Yo!コピペ職人、チェケラッチョ!イエーィ!
includeでコピペするファイルって何なの?
ラップ職人!includeでコピペしているファイルって何なんですか?
私はいったい何のファイルをコピペしているんですか?
Hey、おれっちラップ職人じゃねーぜ、コピペ職人だぜ、Yo-w!
C言語ではヘッダファイルをインクルードしてコピペするんだぜ、Yo!
「ヘッダファイル」というキーワードが出てきました。includeを理解するためには、ヘッダファイルを理解する必要があります。
ヘッダファイルとソースファイル
includeで指定していたファイル名は「stdio.h」「string.h」といった拡張子が「.h」となっているファイルであるという共通点があります。
このファイルを「ヘッダファイル」と呼びます。皆さんが作るC言語のプログラムとは、次のファイルで構成されます。
ヘッダファイルには何が書かれているのか?
皆さんはここまで、main.cというソースファイルに対して、様々な処理を関数として記述してきました。
では、「ヘッダファイル」とは何が書かれているファイルなのでしょう。
ここで一度、関数の記事内容に立ち返ってみます。関数を呼び出す際に関数定義順によって警告やエラーが発生するという注意事項がありました。
これを解決するために、「プロトタイプ宣言」というものを書くことで解決できるとも解説しました。
でも、不思議な点に気づきませんか?
printf関数も関数ですよね。subfunc関数の中からprintf関数を呼び出せているのはなぜでしょう。printf関数のプロトタイプ宣言なんて皆さん書いてないですよね。
この謎を解くのが「stdio.h」なんです。
printf関数のプロトタイプ宣言は、実は「stdio.h」の中に書かれています。つまり、「stdio.h」ををincludeすることで、プロトタイプ宣言がコピー&ペーストされていたのです。
incudeでヘッダファイルをコピペする様子を図解してみた
ラップ職人!コピペって本当にコピペするんですか?本当の本当にコピペするってことなんですか?コピペされるようなイメージとかじゃなくて?
Yo、おまえ、おれっちのことラップ職人だと思ってるな、Yeah!実はちょっとうれしいぜ、Yo!おれっちは本物のコピペ職人、リアルにコピペするのが職人芸だぜ、Yo-w!
コピペするというと「コピーされるようなイメージってことですよね」と思う方がいますが、違います。本当にファイル内容が、そのままコピペされるんです。
ヘッダファイルがインクルードされる図
では、具体的にヘッダファイルというものが、ソースファイルにコピペされる様子を図示化してお見せしましょう。
実際のヘッダファイルの中身は、ここまでに登場した「マクロ定義」「構造体定義」「プロトタイプ宣言」などいくつかの要素が記述されています。
sub.hにこれらが記載されているとしましょう。このsub.hをmain.cからincludeしてみます。
includeでファイル名を指定することで、対象のファイル内容がincludeした箇所にそっくりそのままコピペされます。
このようにC言語における「ヘッダファイル」とは、
includeによってコピペされるために作られたファイル
なのです。
ヘッダファイルは俺っちの相棒とも言えるファイルだぜ、Yow!
includeを使う時に知っておくとよいこと
Yeah、チェケラッチョ職人!includeにまつわる、知っているとお得な情報を教えてください、Yo。
Hey、Yo!おまえもラップ職人、俺も職人っ、負けじとかましてやるぜっ、Yeah!これだけは知っとけ、Yo!
インクルード指定方法の種類
インクルードの指定方法は2種類存在します。次のものです。
#include <ヘッダファイル名> // ①:標準ライブラリ用ヘッダインクルード
#include "ヘッダファイル名" // ②:対象システム固有ヘッダインクルード
「stdio.h」といった開発環境に、あらかじめ用意されたヘッダファイルを指定する場合は①。
皆さんが自分で作成したヘッダファイルは②で指定します。
ヘッダファイル以外でもインクルードできる
実は、include機能は拡張子「.h」以外のファイルもインクルードすることが可能です。
例えば「.txt」ファイルや、何だったら「.c」ファイルだろうとインクルードすることができます。
インクルード機能とは指定されたファイルを単にコピペする機能であり、拡張子は実は何でもよいのです。
ただし、C言語ではソースファイルとヘッダファイルの役割が決まっており、その役割に従った上で「ヘッダファイルをインクルードするようにしようね!」という暗黙のルールがあります。
そのため、基本はヘッダファイルをインクルードすることです。ヘッダファイルの役割は、別の記事にて解説しましょう。
「インクルードパス」はファイルを見るけるための検索場所
「プリプロセッサ」はincludeで指定されたファイルを、いったいどこから見つけているのでしょうか?
皆さんの使っているパソコンには、無数のファイルが存在します。
さすがのプリプロセッサも、パソコンの中の全フォルダをいちいち検索していたら、見つけるだけでも時間が掛かってしまいます。
つまり、includeで指定されたヘッダファイルをコピペするためには、そのファイルがどのフォルダにあるかを知らないとできないのです。
そこで登場するのが「インクルードパス」です。
「インクルードパス」とは、プリプロセッサを動かす時に指定するヘッダファイルの存在するフォルダ場所のことです。
つまり、ヘッダファイルのある場所は開発者である皆さんが、本来指定してあげないといけないということです。
VisualStudioではプロジェクトのコンパイラ(C/C++)のプロパティから設定することができます。プリプロセッサはコンパイラツールに内包されているため、設定はコンパイラに対して行います。
「追加のインクルードディレクトリ」の項目が、インクルードパスの設定場所です。
このようにプリプロセッサにヘッダファイルの場所を指定することを「パスを通す」といいます。
パスを指定することで、プリプロセッサはそのフォルダを順に検索し、見つければそのファイルをインクルードします。
パスは複数設定することができますが、先勝ちとなることに注意しましょう。
統合開発環境を使わない開発スタイルの場合は、自分でパスを通すことが基本になるっちよ。「インクルードパス」これは覚えておくっちよ!
includeでよく発生するビルドエラー対処法
くぅ、ビルドエラーが取り除けません。な、何が起きているんですか、チョモランマ職人!教えてください。
名前が完全に別物になってるっちよ…。落ち込むっちょ。
よくやるパターンは決まってるっちよ、チェケラ!
includeを指定したときにビルドエラーが発生することがあります。
includeの原理や対処方法を身に付けておくと、このような時も冷静に対応することができます。
includeするファイルが見つからないエラー発生時の対処法
ヘッダファイルをインクルードしたときに、次のようなエラーが表示されることがあります。慣れていない方は「うん?何が悪いの?」と戸惑うこともあるでしょう。
fatal error C1083: include ファイルを開けません。'sub.h':No such file or directory
このエラーはinclude指定されたファイルが、見つからない時のエラーメッセージです。次のような観点で疑ってみるとよいでしょう。
疑うべきことその①
includeしようとしているファイルの名前が正しいかを確認しましょう。
ファイル名が間違っているようでは絶対にincludeできません。
疑うべきことその②
インクルードパスが通っていることを確認しましょう。ファイル名が正しくて、このエラーが出ているケースは、ほとんどのケースでインクルードパスが通っていないことが原因です。
統合開発環境を利用している場合は、プロジェクトの中にヘッダファイルが登録されていれば自動でパスが通るはずです。
プロジェクトに対象のヘッダファイルが登録されているかを確認しましょう。
ヘッダファイルは見つかっているが、なぜか目的のヘッダファイルが読み込まれていないような気がする時の対処法
結構稀なのですが、長いこと開発をやっていると、このようなことが起きることがあります。
このような条件が運悪く重なると、「何が起きているんだ?読み込まれているはずなのに、何かがおかしい!」と悩んで時間を浪費することがあります。
こんな時は、ヘッダファイルが正しく読み込まれているかを確認するために、目的のヘッダファイル「sub.h」のファイル名を一時的に「_sub.h」といった別名に変更し、ビルドエラーが発生する細工をします。
この状態でビルドが通るようであれば、意図しない同名ヘッダが読み込まれています。インクルードパスを見直すなどして対処しましょう。
Q&A:includeに関するよくある質問
今日はコピペ職人の俺っちがなんでも答えてやるYo!
Q:インクルードの<ファイル名>と”ファイル名”は何が違うの?
どうして、includeで指定する形式に<>と””の2つがあるんですか?
何が違うのですか?
<>は標準ライブラリ用のヘッダファイルに使用する形式っちよ。<>を使った場合は、開発環境独自の標準ライブラリ用ヘッダファイルの置いてある場所を検索する仕組みになってるんだYo!
この2つの違いはヘッダファイルを探しに行く場所が異なります。”ファイル名”の形式の方がより広範囲で検索を掛けてくれます。
よって、次のようにstdio.hを”ファイル名”の形式にしてもビルドは通ります。
#include "stdio.h"
“ファイル名”の形式で指定した場合は、インクルードパスを通したフォルダが検索対象となります。