C言語基礎|ヘッダとインクルード

C言語の“説明書”を読み込むしくみ

関数を呼び出すときって、コンパイラにも私たちにも「その関数の仕様」が必要でしたよね。
引数は何個? 型は? 返ってくる値は?―これが分からないと、正しく呼び出せません。

じゃあ、printf や scanf みたいな ライブラリ関数 の仕様はどこに書いてあるの?
答えは ヘッダ の中です。たとえば入出力なら stdio.h に、printf などの関数原型宣言がまとまっています。

そして、そのヘッダの内容をプログラムに取り込むのが #include 指令(インクルード) です。
ざっくり言うと「この行の場所に、ヘッダの中身を貼り付ける」みたいなイメージです。

サンプルプログラム

平均値を計算して表示するプログラムを例に解説をします。

プロジェクト名:chap6-13-1 ソースファイル名:chap6-13-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int a, b;
    double avg;

    puts("2つの整数から平均を出します。");
    printf("1つ目の整数:");
    scanf("%d", &a);
    printf("2つ目の整数:");
    scanf("%d", &b);

    avg = (a + b) / 2.0;

    printf("平均は%.1fです。\n", avg);
    return 0;
}

実行例

2つの整数から平均を出します。
1つ目の整数:10
2つ目の整数:25
平均は17.5です。

このプログラムでは printf / scanf / puts を使っています。
だから、それらの仕様(関数原型宣言など)が入っている stdio.h を #include で取り込んでいます。

ヘッダって何?インクルードって何?

用語の整理

用語ざっくり説明代表例中に入っているもの(例)
ヘッダライブラリの“説明書”みたいな集合stdio.hprintf / scanf / puts / putchar の関数原型宣言、型、マクロなど
インクルードヘッダの内容を取り込むこと#include <stdio.h>プログラム側に仕様情報が見えるようになる

この表は「ヘッダ=情報のまとまり」「include=取り込み操作」を混同しないための整理です。

#include 指令の書式と意味

C言語の # で始まる行は、普通の文や式ではなく プリプロセッサ指令(前処理)です。
コンパイル前に、コードを書き換えるような準備をします。

書式(代表)

  • #include <ヘッダ名>
  • #include "ヘッダ名"

何をする“命令”なの?

  • #include <stdio.h> は、処理系(コンパイラ環境)が用意しているヘッダを探して取り込みます。
  • #include "myutil.h" は、自作ヘッダなど、まずは自分のプロジェクト付近を探して取り込みます(その後に標準の探索もする処理系が多いです)。

インクルードは“貼り付け”に近い

インクルードのイメージ(簡略)

書いたコード前処理後のイメージ
#include <stdio.h>(ここに stdio.h の内容が展開されるイメージ)
int main(void) { ... }int main(void) { ... }

本当に全部が目に見える形で貼り付くかどうかは処理系によりますが、学習上は「その場に展開される」と思うと理解しやすいです。

じゃあ、stdio.h の中には何があるの?

stdio.h には、入出力に関する関数の仕様が入っています。
たとえば putchar の関数原型宣言は、処理系によって次のような形で書かれています(例です)。

  • int putchar(int c);
  • int putchar(int __c);

仮引数名は処理系によって違ってOKです。
関数原型宣言では、仮引数名そのものは省略できるので、型さえ合っていれば成立します。

stdio.h を入れないとどうなるの?

よくある結果

状況どうなる?理由
printf を使うのに stdio.h を入れない警告やエラーになりやすいコンパイラが printf の仕様を知らない(型チェックできない)
入出力を一切しないstdio.h は不要そもそも printf 等を呼ばないので仕様が要らない

最近のCコンパイラは、関数の宣言なし呼び出しを厳しく扱うので「入れ忘れ」はバグの入り口になりやすいです。

ヘッダは“ファイル”とは限らない?(用語がヘッダな理由)

教科書などで「ヘッダファイル」と言いがちですが、規格や処理系の都合で、
“必ずしも単独のファイルとして見える形で提供される”とは限りません。

だから厳密には「ヘッダ」と呼ぶのが安全、という考え方です。
学習段階では「だいたいファイルと思ってOK」ですが、呼び方の背景を知っておくと混乱しません。

依存しすぎる関数は再利用しにくい(汎用性の話)

ここ、けっこう大事な話です。

たとえば「どこかにある配列 tensu と、どこかにある NUMBER を前提にして最大値を返す」みたいな関数は、別の場面で使い回しにくいです。

再利用しやすい関数・しにくい関数

関数の形再利用性理由
max_of_array(int a[], int n)高い配列と要素数を引数で受け取るので、何でも扱える
top(void) が外の配列やマクロに依存低い特定の変数名・要素数に縛られてしまう

「外部の情報に依存しない」「必要な情報は引数でもらう」
これが、関数を部品として強くするコツです。

ダメな例:外側に依存してる関数(再利用しにくい)

たとえば、こういう形。

#define NUMBER 5
int tensu[NUMBER];

int top(void)
{
    int max = tensu[0];
    for (int i = 1; i < NUMBER; i++) {
        if (tensu[i] > max) max = tensu[i];
    }
    return max;
}

これの問題点

top()
  ├─ tensu という“特定の名前”が必須
  └─ NUMBER という“固定サイズ”が必須

つまり top は tensu と NUMBER がある世界でしか生きられない んです。

良い例:依存を引数にする(汎用化)

依存してるものを全部「引数で受け取る」ようにすると、一気に部品になります。

int max_of_array(const int a[], int n)
{
    int max = a[0];
    for (int i = 1; i < n; i++) {
        if (a[i] > max) max = a[i];
    }
    return max;
}

何が良くなった?

  • 配列名が何でもOK(tensu でも scores でも)
  • 要素数が何でもOK(5でも100でも)
  • 他の配列と同時に使える。
  • 関数単体でテストできる。

重要ポイント(やわらかめに要約)

  • ヘッダはライブラリの仕様書みたいなもの(関数原型宣言などが入ってる)
  • #include はヘッダの内容を取り込む指令(前処理)
  • < > は標準ヘッダ向け、" " は自作ヘッダ向けが基本
  • 関数は外部の変数やマクロに頼りすぎると、再利用しにくくなる。