
はじめての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.cmath_utils.cmath_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_sample2. 3つのファイルを作成する
次の3つのファイルを同じディレクトリに保存します。
main.cmath_utils.cmath_utils.h
3. コンパイルする
clang main.c math_utils.c -o app4. 実行する
./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);
#endifmath_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.c で add の宣言が見えていません。
対処: #include "math_utils.h" を書きます。
undefined symbols for architecture …
原因: 関数の宣言は見えているが、関数の定義がコンパイルに含まれていません。
対処: math_utils.c もコンパイルコマンドに含めます。
clang main.c math_utils.c -o appfatal error: ‘math_utils.h’ file not found
原因: ヘッダファイルが見つかりません。
対処: main.c と math_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);
#endifmessage.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 を扱います。


