C言語 include【インクルードで起こる変化の正体を解説】

C言語
この記事は約12分で読めます。

こんにちは、ナナです。

プリプロセッサ3大機能のうちの1つ「include」機能について解説しましょう。

プリプロセッサ3大機能
  • #define マクロ定義
  • #include インクルード
  • #ifdef 条件コンパイル

「include」機能を使用することで、他の人が作ったプログラムを利用することができ、皆さんが作ったプログラムを他の人に利用してもらうこともできるようになります。

「include」機能を理解するためには、最低限「関数」に関しての知識は得ておきましょう。これが理解する上での最低ラインの知識です。

本記事では次の疑問点を解消する内容となっています。

本記事で学習できること
  • includeの意味と役割とは?
  • includeで指定するヘッダファイルとは何なのか?
  • 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、それすなわち、include=おれっちってことだぜっ、Yo!コピペ職人、チェケラッチョ!イエーィ!

スポンサー

includeでコピペするファイルって何なの?

ラップ職人!includeでコピペしているファイルって何なんですか?

私はいったい何のファイルをコピペしているんですか?

職人

Hey、おれっちラップ職人じゃねーぜ、コピペ職人だぜ、Yo-w!

C言語ではヘッダファイルをインクルードしてコピペするんだぜ、Yo!

「ヘッダファイル」というキーワードが出てきました。includeを理解するためには、ヘッダファイルを理解する必要があります。

ヘッダファイルとソースファイル

includeで指定していたファイル名は「stdio.h」「string.h」といった拡張子が「.h」となっているファイルであるという共通点があります。

このファイルを「ヘッダファイル」と呼びます。皆さんが作るC言語のプログラムとは、次のファイルで構成されます。

C言語プログラムの構成ファイル
  • ソースファイル 拡張子「.c」
  • ヘッダファイル 拡張子「.h」

ヘッダファイルには何が書かれているのか?

皆さんはここまで、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"

“ファイル名”の形式で指定した場合は、インクルードパスを通したフォルダが検索対象となります。