第18回|C言語で複数ファイル構成とヘッダファイルを学ぶ:プログラムを分けて管理する

当サイトでは、コンテンツの一部に広告を掲載しています。
第18回|C言語で複数ファイル構成とヘッダファイルを学ぶ:プログラムを分けて管理する

はじめてのC言語 | 第18回

目次

はじめに

前回は、ファイル入出力の基本を学びました。
今回は、C言語のプログラムを複数のファイルに分ける方法を学びます。

これまでのサンプルコードは、基本的に1つの .c ファイルにすべてを書いていました。
プログラムが大きくなると、関数を別ファイルに分けたほうが管理しやすくなります。

この回の目的は次の5点です。

  • .c ファイルと .h ファイルの役割を理解する
  • 関数の定義と宣言の違いを整理する
  • ヘッダファイルを使う理由を学ぶ
  • 複数の .c ファイルをまとめてコンパイルする
  • 小さなプログラムを分けて管理する考え方を身につける

なぜファイルを分けるのか

プログラムが小さいうちは、1つのファイルにすべてを書いても問題ありません。
しかし、関数が増えてくると、1つのファイルが長くなり、どこに何があるか分かりにくくなります。

たとえば、次のように役割ごとに分けることができます。

  • main.c : プログラム全体の流れを書く
  • math_utils.c : 計算用の関数を書く
  • math_utils.h : 計算用の関数宣言を書く

このように分けると、関数の役割が見えやすくなります。

.c ファイルと .h ファイル

C言語では、主に次の2種類のファイルを使います。

  • .c ファイル : 関数の中身を書くファイル
  • .h ファイル : 関数の宣言を書くファイル

関数の中身を「定義」と呼びます。
関数の名前、引数、戻り値だけを知らせるものを「宣言」と呼びます。

たとえば次は関数の定義です。

int add(int a, int b) {
    return a + b;
}

次は関数の宣言です。

int add(int a, int b);

宣言の末尾には ; が必要です。

3つのファイルを作る

今回は、次の3つのファイルを作ります。

  • main.c
  • math_utils.c
  • math_utils.h

main.c から add 関数を呼び出し、その中身は math_utils.c に書きます。

ヘッダファイルを作る

math_utils.h

#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int add(int a, int b);

#endif

このファイルには、add 関数の宣言を書いています。

関数の中身を書く

math_utils.c

#include "math_utils.h"

int add(int a, int b) {
    return a + b;
}

このファイルには、add 関数の定義を書いています。
#include "math_utils.h" によって、宣言と定義が一致しているかを確認しやすくなります。

main.c から関数を呼び出す

main.c

#include <stdio.h>
#include "math_utils.h"

int main(void) {
    int result;

    result = add(3, 5);

    printf("%d\n", result);

    return 0;
}

main.c では、#include "math_utils.h" を書くことで、add 関数を呼び出せるようにしています。

実行手順

1. 作業ディレクトリを作って移動する

mkdir multi_file_sample
cd multi_file_sample

2. 3つのファイルを作成する

次の3つのファイルを同じディレクトリに保存します。

  • main.c
  • math_utils.c
  • math_utils.h

3. コンパイルする

clang main.c math_utils.c -o app

4. 実行する

./app

実行結果:

8

コードの読み方

#include “math_utils.h”

#include "math_utils.h"

自分で作ったヘッダファイルを読み込むときは、" を使います。
標準ライブラリのヘッダでは <stdio.h> のように < > を使いました。

#ifndef と #define

#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int add(int a, int b);

#endif

これは、同じヘッダファイルが重複して読み込まれることを防ぐための書き方です。
インクルードガードと呼びます。

初学者の段階では、ヘッダファイルにはこの形を書いておく、と覚えて問題ありません。

clang main.c math_utils.c -o app

clang main.c math_utils.c -o app

複数の .c ファイルを使う場合は、コンパイル時に必要な .c ファイルをすべて指定します。
.h ファイルは #include で読み込まれるため、通常はコンパイルコマンドに直接書きません。

関数を追加する

次に、引き算の関数 sub を追加します。

math_utils.h

#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int add(int a, int b);
int sub(int a, int b);

#endif

math_utils.c

#include "math_utils.h"

int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

main.c

#include <stdio.h>
#include "math_utils.h"

int main(void) {
    printf("%d\n", add(3, 5));
    printf("%d\n", sub(10, 4));

    return 0;
}

コンパイル:

clang main.c math_utils.c -o app

実行結果:

8
6

ヘッダファイルに何を書くか

ヘッダファイルには、他の .c ファイルから使いたい情報を書きます。
この回では、関数宣言を書きます。

int add(int a, int b);
int sub(int a, int b);

関数の中身は .c ファイルに書きます。
ヘッダファイルに関数の中身まで書くのは、この段階では避けます。

初心者がつまずきやすい点

.h ファイルだけでは実行ファイルは作れない

.h ファイルには宣言を書きます。
関数の中身は .c ファイルに必要です。

math_utils.h があっても、math_utils.c をコンパイルに含めなければ、関数の中身が見つかりません。

コンパイル時に .c ファイルを指定し忘れる

次のように main.c だけを指定すると、add の中身が見つからないことがあります。

clang main.c -o app

正しくは、必要な .c ファイルをすべて指定します。

clang main.c math_utils.c -o app

#include の ” と < > を混同する

標準ライブラリは次のように書きます。

#include <stdio.h>

自分で作ったヘッダファイルは次のように書きます。

#include "math_utils.h"

関数宣言の末尾に ; を忘れる

ヘッダファイルに書く関数宣言には、末尾の ; が必要です。

int add(int a, int b);

よくあるエラー

implicit declaration of function ‘add’

原因: main.cadd の宣言が見えていません。
対処: #include "math_utils.h" を書きます。

undefined symbols for architecture …

原因: 関数の宣言は見えているが、関数の定義がコンパイルに含まれていません。
対処: math_utils.c もコンパイルコマンドに含めます。

clang main.c math_utils.c -o app

fatal error: ‘math_utils.h’ file not found

原因: ヘッダファイルが見つかりません。
対処: main.cmath_utils.h が同じディレクトリにあるか確認します。

練習用コード

乗算関数を追加する

math_utils.h に宣言を追加します。

int mul(int a, int b);

math_utils.c に定義を追加します。

int mul(int a, int b) {
    return a * b;
}

main.c から呼び出します。

printf("%d\n", mul(4, 6));

実行結果:

24

文字列表示用のファイルを作る

message.h

#ifndef MESSAGE_H
#define MESSAGE_H

void print_message(void);

#endif

message.c

#include <stdio.h>
#include "message.h"

void print_message(void) {
    printf("hello\n");
}

main.c

#include "message.h"

int main(void) {
    print_message();

    return 0;
}

コンパイル:

clang main.c message.c -o app

まとめ

今回のポイントは次のとおりです。

  • .c ファイルには関数の定義を書く
  • .h ファイルには関数の宣言を書く
  • 自分で作ったヘッダファイルは #include "ファイル名.h" で読み込む
  • ヘッダファイルにはインクルードガードを書く
  • 複数の .c ファイルを使う場合は、コンパイル時に必要な .c ファイルを指定する
  • 関数宣言と関数定義の引数や戻り値は一致させる
  • ファイルを分けると、役割ごとに管理しやすくなる

この回では、C言語のプログラムを複数ファイルに分ける方法を学びました。
.c ファイルに関数の中身を書き、.h ファイルに関数宣言を書くことで、別ファイルから関数を使いやすくなります。

次回予告

次は、動的メモリ確保の基本を学びます。
実行時に必要なメモリを確保する malloc と、使い終わったメモリを解放する free を扱います。

あわせて読みたい
第19回|C言語で動的メモリ確保の基本を学ぶ:必要な分だけメモリを確保する はじめてのC言語 | 第19回 はじめに 前回は、複数ファイル構成とヘッダファイルを学びました。 今回は、C言語の「動的メモリ確保」を学びます。 これまでの配列は、プ...

さらに学びたいあなたへ

用途ごとに選ぶ Linux のおすすめ本

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

のいのアバター のい UNIX Cafe マスター

Macintosh Color Classicから始まった旅は、長いWindows時代を経て、Windows10のサポート終了をきっかけにUNIXの世界へ戻ってきました。UNIX Cafeでは、UNIX・Linux・そしてMacな世界を、むずかしい言葉を使わず、物語のように書いています。プログラミングは、アイデアをコンピューターに伝えるための言葉です。簡単な単語と文法を覚えれば、誰でもコマンドを使えます。ぜひ一度、やさしいプログラミングの世界をのぞいてみてください。

Created by UNIX Cafe

目次