C言語の基本|C言語の#includeとヘッダファイルのしくみ

いつも書いているその1行には意味がある。#includeを理解して、C言語のコンパイルの流れを見通そう

C言語のプログラムでは、ソースコードの先頭に次のような記述をよく書きます。

#include <stdio.h>

printfで文字列を表示するときや、scanfで値を入力するときに、何気なく書いてきた人も多いでしょう。

しかし、#includeは単なる決まり文句ではありません。関数、型、マクロなどの情報をプログラムに取り込み、コンパイラがコードを正しく確認できるようにするための大切な仕組みです。

例えば、printfを使うときにはstdio.hをインクルードします。stdio.hには、printfがどのような形式で呼び出される関数なのかという宣言が含まれています。

この宣言があることで、コンパイラはprintfの呼び出し方を確認できます。引数の指定に問題がある場合には、警告やエラーを表示してプログラマーに知らせることができます。

また、#includeは、プログラムの実行中にライブラリを読み込む処理ではありません。コンパイルが始まる前の前処理で、指定したヘッダの内容を取り込むための前処理指令です。

この仕組みを理解すると、次のような疑問が解消されていきます。

  • なぜprintfを使うときにstdio.hが必要なのか
  • ヘッダファイルには何が記述されているのか
  • #includeはいつ処理されるのか
  • 関数の宣言と関数本体は何が違うのか
  • 山かっこと二重引用符をどのように使い分けるのか
  • #includeを書けば関数本体まで取り込まれるのか

ここでは、ヘッダファイルの役割、前処理の流れ、主な標準ヘッダ、山かっこと二重引用符の使い分けについて、順番に見ていきましょう。

ヘッダファイルとは

ヘッダファイルとは、関数の宣言、マクロ、型定義など、ほかのソースコードから機能を利用するために必要な情報をまとめたファイルです。

一般的なヘッダファイルの名前には、headerの頭文字を表す.hという拡張子が使われます。

代表的な標準ヘッダには、次のようなものがあります。

  • stdio.h
  • string.h
  • ctype.h
  • math.h
  • stdlib.h
  • time.h

例えば、printfを使うプログラムでは、次のようにstdio.hをインクルードします。

#include <stdio.h>

この記述によって、stdio.hが提供するprintfなどの宣言をプログラムで利用できるようになります。

ヘッダファイルは、必要な機能を利用するための案内書や入口のような存在です。通常、標準ライブラリ関数の処理本体がそのまま書かれているのではなく、その関数をどのような形式で呼び出すのかを知らせる情報がまとめられています。

ヘッダファイルの主な役割

ヘッダファイルが提供する主な情報は、関数の宣言、マクロ、型定義の3つです。

ヘッダファイルの役割内容
関数の宣言を提供する関数名、引数、戻り値の型をコンパイラに知らせるprintf、strlen、sqrt
マクロを提供する定数や簡単な処理を名前で利用できるようにするEOF
型定義を提供する特定の機能で必要になる型を利用できるようにするsize_t、FILE

関数の宣言を提供する

関数の宣言は、関数の名前、受け取る引数、返す値の型などをコンパイラに知らせます。関数プロトタイプと呼ばれることもあります。

例えば、文字列の長さを求めるstrlenは、string.hで宣言されています。

その宣言は、次のような形式です。

size_t strlen(const char *s);

この宣言から、コンパイラは次の情報を確認できます。

  • 関数名はstrlenである
  • 引数として文字列の先頭を表すポインタを受け取る
  • 戻り値はsize_t型である
  • 引数が指している文字列を関数内で変更しない

適切な関数の宣言が見えていれば、コンパイラは関数の呼び出し方を確認しやすくなります。

マクロを提供する

ヘッダファイルには、マクロが定義されていることもあります。

マクロを利用すると、定数や簡単な処理を意味の分かる名前で表現できます。

例えば、stdio.hでは、入力の終わりや入力エラーを表す値としてEOFが提供されています。

if (ch == EOF) {
    /* 入力の終わり、または入力エラー */
}

EOFの具体的な数値をプログラムに直接書くのではなく、意味を表すEOFを使うことで、処理の目的が分かりやすくなります。

型定義を提供する

ヘッダファイルには、特定の機能を利用するときに必要な型が定義されていることもあります。

主な用途
size_t配列の大きさや文字列の長さなどを表す
FILEファイルを扱うための情報を表す

strlenの戻り値にはsize_tが使われます。また、ファイル入出力ではFILE型へのポインタを利用します。

このように、ヘッダファイルは関数の宣言だけでなく、その機能に関連する型やマクロも提供しています。

図1:ヘッダファイルが提供する3つの情報

この図から分かること

ヘッダファイルには、関数の宣言だけでなく、マクロや型定義もまとめられています。

プログラムで標準ライブラリの機能を使うには、関数名だけを知っていればよいわけではありません。引数、戻り値、関連する型などの情報も必要です。

ヘッダファイルは、これらの情報をプログラムへ提供し、コンパイラが関数の呼び出し方を確認できるようにします。

なぜヘッダファイルが必要なのか

C言語では、関数を適切に呼び出すために、その関数がどのような形式で宣言されているのかをコンパイラに知らせる必要があります。

例えば、printfは、引数として書式文字列を受け取り、その書式に応じて追加の引数を受け取る関数です。

printf("得点は%d点です。\n", score);

stdio.hをインクルードすると、コンパイラはprintfの宣言を確認できます。その情報を基に、関数の呼び出し方に問題がないかを調べます。

ヘッダファイルは、プログラムを正しくコンパイルするための形式的な飾りではありません。関数の誤った使い方を見つけやすくし、安全性を高める役割があります。

ヘッダファイルを読み込まない場合の問題

必要なヘッダファイルをインクルードせずに関数を呼び出すと、次のような問題が発生します。

発生する問題内容
警告やエラーが出る関数の宣言が見つからず、正しくコンパイルできないことがある
引数を確認できない引数の個数や型を適切に確認できなくなる
戻り値を確認できない関数が返す値の型を正しく判断できなくなる
間違いを発見しにくい本来ならコンパイラが見つけられる問題を確認しにくくなる
処理系による違いが出るコンパイラやC言語規格の設定によって結果が変わる可能性がある

現在のC言語では、関数を適切に宣言せずに呼び出すことは正しい書き方ではありません。

開発環境によっては警告だけで実行ファイルが作られる場合もあります。しかし、警告が出ても動作したから問題がないとは限りません。

関数を使うときは、その関数が宣言されているヘッダを正しくインクルードしましょう。

関数の宣言と関数本体の違い

ヘッダファイルの役割を理解するには、関数の宣言と関数本体の違いを知ることが大切です。

種類役割
関数の宣言関数名、引数、戻り値の型をコンパイラに知らせる
関数本体関数が実際に行う処理を記述する

例えば、商品の合計金額を計算する関数の宣言は、次のように記述できます。

int calculate_total(int price, int count);

この宣言から、calculate_totalは2つのint型の値を受け取り、int型の値を返す関数であることが分かります。

関数本体は、次のように記述します。

int calculate_total(int price, int count)
{
    return price * count;
}

関数の宣言は、関数の使い方を知らせる情報です。関数本体には、実際に実行される処理が記述されています。

ヘッダファイルには、一般的に関数の宣言を記述します。実際の処理を行う関数本体は、別のソースファイルやライブラリに用意されます。

stdio.hをインクルードしても、printfの処理本体がそのままソースコードへ書き込まれるわけではありません。stdio.hからは、printfを正しく呼び出すための宣言などが提供されます。

#include指令とは

#includeは、指定したヘッダやファイルの内容を取り込むための前処理指令です。

前処理指令は、行の先頭に#を付けて記述します。

#include <stdio.h>

#includeは、C言語の関数ではありません。そのため、関数呼び出しのように丸かっこを使いません。

次の書き方は誤りです。

#include(<stdio.h>)

また、#includeの行末にはセミコロンを付けません。

正しい書き方は次のとおりです。

#include <stdio.h>

次のようにセミコロンを付ける必要はありません。

#include <stdio.h>;

前処理とは

前処理とは、コンパイラがC言語のソースコードを本格的に解析する前に行う処理です。

前処理では、#includeなどの前処理指令が処理されます。

ソースファイルに次の記述があるとします。

#include <stdio.h>

前処理では、この位置にstdio.hが提供する内容を取り込んだ状態を作り、その結果をコンパイラへ渡します。

処理の流れは次のように考えると分かりやすいでしょう。

ソースファイルに#includeを書く
                ↓
前処理でヘッダの内容を取り込む
                ↓
必要な宣言や型を含んだ状態にする
                ↓
コンパイラがソースコードを解析する

標準ヘッダは、必ずしもstdio.hという名前の物理的なファイルとして保存されているとは限りません。C言語の処理系が、標準ヘッダとして必要な情報を提供します。

学習の初めは、stdio.hを指定すると、標準入出力に必要な情報が前処理で利用できるようになると考えておけばよいでしょう。

#includeは実行時の処理ではない

#includeについて特に重要なのは、プログラムの実行中にヘッダファイルを読み込む処理ではないという点です。

#includeは、コンパイル前の前処理で働きます。そのため、完成したプログラムを実行するたびにstdio.hを読み込んでいるわけではありません。

処理実行される時期役割
#includeコンパイルより前ヘッダの内容を取り込む
printfプログラムの実行中文字列や数値を出力する

#includeとprintfでは、働く時期も役割も異なります。

  • #includeはプログラムを作る準備として働く
  • printfは完成したプログラムの実行中に働く
  • #includeは前処理指令である
  • printfは標準ライブラリ関数である

この違いを押さえると、#includeが関数ではなく、前処理の段階で扱われる指令であることを理解しやすくなります。

前処理から実行ファイルができるまで

C言語のソースファイルから実行ファイルが作られるまでには、いくつかの段階があります。

段階主な処理
前処理#includeなどの前処理指令を処理する
コンパイルC言語のソースコードを解析して変換する
アセンブルアセンブリコードからオブジェクトファイルを作る
リンクオブジェクトファイルと必要なライブラリを結びつける

開発環境によっては、これらの処理全体を広い意味でコンパイルと呼ぶこともあります。

図2:前処理から実行ファイルまでの流れ

この図から分かること

#includeは、ビルド工程の最初に行われる前処理で扱われます。ヘッダの情報を取り込んだ結果がコンパイルされ、オブジェクトファイルが作られます。

その後、リンクによってオブジェクトファイルと必要なライブラリが結びつき、実行ファイルが完成します。

ヘッダファイルが提供する宣言と、ライブラリが提供する関数本体は役割が異なります。#includeによって宣言などの情報を取り込み、リンクによって実際の関数本体と結びつくという流れです。

printfを使ったプログラムで確認しよう

ここでは、stdio.hをインクルードし、printfで学習メッセージを表示するプログラムを確認します。

stdio.hを読み込んでメッセージを表示するプログラム

ファイル名:12_2_1.c

#include <stdio.h>

int main(void)
{
    /* 前処理の学習開始を表示する */
    printf("#includeのしくみを確認しましょう。\n");

    return 0;
}
実行結果の例
#includeのしくみを確認しましょう。

プログラムの詳しい解説

stdio.hをインクルードする部分

#include <stdio.h>

このプログラムではprintfを使うため、printfが宣言されているstdio.hをインクルードしています。

#から始まっているため、通常のC言語の文ではなく前処理指令です。また、行末にセミコロンは付けません。

前処理が行われると、stdio.hから提供されるprintfの宣言などを利用できる状態になります。その結果、コンパイラはprintfの呼び出し方を確認できます。

main関数を定義する部分

int main(void)

mainは、プログラムの実行が始まる関数です。

intは、mainがint型の値を返すことを表します。voidは、このmainが引数を受け取らない形式であることを示します。

printfでメッセージを表示する部分

printf("#includeのしくみを確認しましょう。\n");

printfは、指定した書式に従って文字列や値を出力する標準ライブラリ関数です。

この例では、二重引用符で囲まれた文字列を画面に表示します。文字列の末尾にある\nは改行を表します。

printfは関数なので、呼び出しの行末にはセミコロンが必要です。

#includeとprintfの違いを整理すると、次のようになります。

記述種類セミコロン働く時期
#includeとstdio.hの指定前処理指令付けないコンパイル前
printfの呼び出し関数呼び出し付けるプログラムの実行中

プログラムを終了する部分

return 0;

return 0によってmain関数を終了し、プログラムが正常に終了したことを実行環境へ伝えます。

stdio.hをインクルードしなかった場合

プログラムから次の行を取り除いたとします。

#include <stdio.h>

すると、printfを呼び出しているにもかかわらず、printfの適切な宣言が見つからない状態になります。

コンパイラはprintfの名前を見つけても、正しい呼び出し方を確認できません。そのため、関数が宣言されていないことを示す警告やエラーが発生します。

コンパイラによって表示内容は異なりますが、主に次のような問題が指摘されます。

  • printfの宣言が見つからない
  • printfが暗黙的に宣言されている
  • stdio.hをインクルードする必要がある
  • 組み込み関数の宣言と一致していない

警告だけで実行ファイルが作られる場合でも、ヘッダファイルを省略してよいわけではありません。printfを使う場合は、stdio.hを正しくインクルードしましょう。

主な標準ヘッダ

C言語には、目的ごとに標準ヘッダが用意されています。

標準ヘッダ主な内容関数の例
stdio.h標準入出力やファイル入出力printf、scanf、puts、fgets
string.h文字列やメモリの操作strlen、strcpy、strcmp
ctype.h文字の判定や変換isalpha、isdigit、toupper
math.h数学計算sqrt、pow、sin
stdlib.hメモリ管理、数値変換、乱数などmalloc、free、rand
time.h日付や時刻の処理time、clock、strftime

stdio.h

stdio.hは、標準入出力やファイル入出力に関する機能を提供します。

#include <stdio.h>

主な関数は次のとおりです。

  • printf
  • scanf
  • puts
  • fgets
  • fopen
  • fclose

画面への出力、キーボードからの入力、ファイルの読み書きなどで使用します。

string.h

string.hは、文字列やメモリを操作する関数の宣言を提供します。

#include <string.h>

主な関数は次のとおりです。

  • strlen
  • strcpy
  • strcmp
  • strcat
  • strchr

C言語では文字列をchar型の配列として扱うため、string.hの関数が多くの場面で役立ちます。

ctype.h

ctype.hは、文字の種類を判定したり、英字の大文字と小文字を変換したりする関数の宣言を提供します。

#include <ctype.h>

主な関数は次のとおりです。

  • isalpha
  • isdigit
  • isspace
  • toupper
  • tolower

入力された文字が数字かどうかを確認したい場合や、英字の表記を統一したい場合に利用します。

math.h

math.hは、数学計算を行う関数の宣言を提供します。

#include <math.h>

主な関数は次のとおりです。

  • sqrt
  • pow
  • sin
  • cos

開発環境によっては、math.hの関数を使うプログラムをビルドするときに、数学ライブラリをリンクする指定が必要になる場合があります。

stdlib.h

stdlib.hは、さまざまな汎用機能に関する宣言や型を提供します。

#include <stdlib.h>

主な関数は次のとおりです。

  • malloc
  • free
  • rand
  • srand
  • strtol

動的メモリ、擬似乱数、文字列から数値への変換など、幅広い処理で使用します。

time.h

time.hは、時刻や処理時間を扱う機能を提供します。

#include <time.h>

主な関数は次のとおりです。

  • time
  • clock
  • difftime
  • strftime

現在時刻の取得や処理時間の測定などに利用します。

使用する機能とヘッダの関係

関数を使うときは、その関数がどの標準ヘッダで宣言されているのかを確認します。

やりたいこと使用する関数インクルードするヘッダ
書式を指定して出力するprintfstdio.h
文字列の長さを調べるstrlenstring.h
数字文字か判定するisdigitctype.h
平方根を求めるsqrtmath.h
メモリを動的に確保するmallocstdlib.h
現在時刻を取得するtimetime.h

最初からすべての標準ヘッダを暗記する必要はありません。

関数を使うたびに、次の3つを組み合わせて確認すると覚えやすくなります。

  • 何を行いたいのか
  • どの関数を使うのか
  • どのヘッダをインクルードするのか

例えば、文字列の長さを調べたい場合は、次のように考えます。

文字列の長さを調べたい
          ↓
strlenを使う
          ↓
string.hをインクルードする

山かっこと二重引用符の違い

#includeには、主に2つの書き方があります。

#include <ヘッダ名>
#include "ファイル名"

どちらも指定したヘッダやファイルを取り込む書き方ですが、主な用途と検索方法が異なります。

山かっこを使う場合

山かっこを使う形式は、主に標準ヘッダをインクルードするときに使用します。

#include <stdio.h>
#include <stdlib.h>

この形式では、処理系が定めた標準的な検索場所からヘッダを探します。

stdio.hやstdlib.hなど、C言語の処理系が提供する標準ヘッダには、基本的に山かっこを使います。

二重引用符を使う場合

二重引用符を使う形式は、主に自分で作成したヘッダファイルをインクルードするときに使用します。

#include "myfunc.h"

この形式では、処理系が定めた方法によってファイルを検索します。一般的には、ソースファイルに近い場所や、ユーザーが指定した検索場所などを先に調べます。

目的のファイルが見つからなかった場合は、山かっこの形式と同様の場所を検索する処理系もあります。

具体的な検索順序は、コンパイラや開発環境によって異なる場合があります。基本的には、標準ヘッダには山かっこ、自作ヘッダには二重引用符を使用すると覚えておきましょう。

2つの書き方の比較

書き方主な用途検索方法の考え方
山かっこを使う形式標準ヘッダ処理系が定めた標準的な場所から探す
二重引用符を使う形式自作ヘッダファイルソースファイルに近い場所などを優先して探すことが多い

この使い分けには、単に見た目を整える以上の意味があります。

標準ヘッダに二重引用符を使うと、環境によっては同じ名前の自作ファイルが先に見つかり、本来とは異なるヘッダが取り込まれる可能性があります。

反対に、自作ヘッダへ山かっこを使うと、検索場所の設定によっては目的のファイルを見つけられないことがあります。

目的に応じて適切な形式を使うことで、どのヘッダを取り込みたいのかが明確になります。

図3:標準ヘッダと自作ヘッダの検索方法

この図から分かること

山かっこと二重引用符では、ヘッダを探すときの考え方が異なります。

山かっこは、stdio.hなどの標準ヘッダに使用します。二重引用符は、myfunc.hなど、自分で作成したヘッダファイルに使用します。

この使い分けによって、処理系がどこからヘッダを探せばよいのかを伝えられます。また、ソースコードを読む人にも、標準ヘッダなのか、自作ヘッダなのかが分かりやすくなります。

自作ヘッダファイルを使う場面

学習の初めは、stdio.hやstring.hなどの標準ヘッダを利用することが中心です。

プログラムが大きくなると、自分で作成した関数を複数のソースファイルから使いたい場面が出てきます。そのようなときは、関数の宣言を自作ヘッダファイルにまとめます。

例えば、計算に関する関数をまとめたmyfunc.hがある場合は、次のようにインクルードします。

#include "myfunc.h"

myfunc.hには、関数の宣言などを記述します。

int calculate_total(int price, int count);

実際の関数本体は、別のソースファイルに記述します。

int calculate_total(int price, int count)
{
    return price * count;
}

ファイルごとの役割は、次のように分けられます。

ファイル主な役割
myfunc.h関数の宣言などを記述する
myfunc.c関数本体を記述する
main.c関数を呼び出して利用する

自作ヘッダを使う詳しい方法は、複数ファイルに分割したプログラムを学ぶときに扱います。

ここでは、標準ヘッダだけでなく、自分で作成したヘッダファイルも#includeで取り込めることを押さえておきましょう。

#includeだけで関数本体が用意されるわけではない

#includeは、ヘッダが提供する宣言や型などの情報を取り込む前処理指令です。関数の処理本体まで、すべて#includeによって用意されるわけではありません。

標準ライブラリ関数を使う流れは、次のように整理できます。

ヘッダをインクルードする
            ↓
関数の宣言などが利用できる
            ↓
コンパイラが呼び出し方を確認する
            ↓
リンクでライブラリの関数本体と結びつく
            ↓
実行ファイルが完成する

例えば、stdio.hをインクルードすると、printfを正しく呼び出すための情報が提供されます。その後、リンクによってprintfの実際の処理とプログラムが結びつきます。

ヘッダとライブラリの役割は、次のように分けられます。

  • ヘッダは関数の使い方を知らせる
  • ライブラリは関数の実際の処理を提供する
  • 前処理ではヘッダの情報を取り込む
  • リンクでは関数本体とプログラムを結びつける

この区別が分かると、#includeだけでなく、コンパイルやリンクの意味も理解しやすくなります。

学習するときに意識したいこと

#includeとヘッダファイルを学ぶときは、次の3点をつなげて考えることが大切です。

学習する項目意識したいこと
#includeの意味コンパイル前の前処理でヘッダの内容を取り込む
ヘッダファイルの役割関数の宣言、マクロ、型定義などを提供する
形式の使い分け標準ヘッダには山かっこ、自作ヘッダには二重引用符を使う

プログラムで関数を使うときは、次の順番で確認するとよいでしょう。

  • 使いたい関数を確認する
  • その関数が宣言されているヘッダを調べる
  • ソースファイルの先頭付近でヘッダをインクルードする
  • 標準ヘッダか自作ヘッダかによって形式を使い分ける
  • コンパイラが表示する警告やエラーを確認する

例えば、文字列の長さを求める場合は、strlenを使います。strlenはstring.hで宣言されているため、プログラムの先頭付近でstring.hをインクルードします。

#include <string.h>

このように、使用する関数と必要なヘッダを組み合わせて覚えると、適切なヘッダを選びやすくなります。

#includeは、ただソースコードの先頭に書く決まり文句ではありません。前処理で必要な情報を取り込み、その後のコンパイルで関数の呼び出し方を確認できるようにする重要な仕組みです。

標準ライブラリ関数を学ぶときには、関数の名前や使い方だけでなく、どのヘッダで宣言されているのかも一緒に確認していきましょう。