C初心者が知っておきたいヘッダーファイルとリンクの基礎知識:目指せ! Cプログラマ(終)(4/4 ページ)
プログラミング言語の基本となる「C」の正しい文法や作法を身に付ける入門連載です。今回は、ヘッダーファイルとリンクを中心に、翻訳単位、ファイル有効範囲、外部定義と仮定義、外部結合と内部結合、結合と記憶域期間、インライン関数の結合、static、extern、inlineなどについても解説。
ヘッダーファイルにまとめておくと便利なもの
先ほどの「inline」の例で、関数「half」のまったく同じ宣言が「main.c」と「test.c」の2箇所にありました。
inline double half(double arg) {
return arg / 2.0;
}
このように複数のファイルで同じ内容を参照する場合、それをヘッダーファイルにまとめられます。ヘッダーファイルを用意して、各ソースファイルはそれを参照するように整理すれば、修正があっても1カ所で済ませることができ、開発がしやすくなります。
#ifndef HALF_H_
#define HALF_H_
inline double half(double arg) {
return arg / 2.0;
}
#endif // HALF_H_
#include <stdio.h>
#include <stdlib.h>
#include "half.h"
void test(void);
int main(void) {
printf("1 / 2 = %f\n", half(1.0));
test();
return EXIT_SUCCESS;
}
#include <stdio.h>
#include "half.h"
extern inline double half(double arg);
void test(void) {
printf("3 / 2 = %f\n", half(3.0));
}
ヘッダーファイルの最初と最後にあるマクロは、ヘッダーファイルの内容が重複定義されないようにするためのものです。詳しくは前回を参照してください。
このようにしておくと、「half」関数に変更があった場合でも「half.h」を変更するだけで済みます。インライン関数の他にも、次のようなものをヘッダーファイルに書いておくと便利です。
- 必要なヘッダーファイルのインクルード
- 定数マクロ
- 関数型マクロ
- 関数プロトタイプ
- 型定義(typedefやstruct、enumなど)
よく行われるのは、モジュール(ソースファイル)の外部インターフェースをヘッダーファイルにまとめておき、その機能を利用したい別のモジュールがそのヘッダーファイルをインクルードできるようにしておくというものです。
次の例を参考にしてみてください。
#include "module1.h"
// このオブジェクトは外部から参照できる。
int module_value = 1;
// この関数は外部から呼ばれる。
int module_func(const struct STR *p) {
/* ... */
return MODULE1_ERROR_SUCCESS;
}
#ifndef MODULE1_H_
#define MODULE1_H_
// 外部からオブジェクト module_value を参照できるようにする。
extern int module_value;
// 構造体 STR を宣言できるようにする。
typedef struct STR_t {
int x, y;
} STR;
// 関数 module_func が返すコードを定義する。
#define MODULE1_SUCCESS (0x0000)
#define MODULE1_ERROR_UNKNOWN (0x0001)
#define MODULE1_ERROR_INVALID (0x0002)
// 外部から関数 module_func を参照できるようにする。
int module_func(const struct STR *p);
#endif // MODULE1_H_
#include <stdlib.h>
#include "module1.h"
int main(void) {
// modulde1 の module_value の値を参照する。
printf("module_value = %d\n", module_value);
// modulde1 の module_func 関数を呼び出す。
STR str = { .x = 200, .y = 100};
int code = module_func(str);
printf("result = %d\n", code);
return EXIT_SUCCESS;
}
今回学んだことのまとめ
- ソースファイルと、そこからインクルードされるヘッダーを全て合わせたものを「翻訳単位」と呼ぶ
- 別の翻訳単位の識別子を参照できるようにする処理を「リンク」(あるいは「結合」)と呼ぶ
- 異なる識別子は結合されない
- ファイル有効範囲を持ち、宣言と同時に初期化されている宣言を「外部定義」と呼び、実体が用意される
- ファイル有効範囲を持ち、宣言と同時に初期化が行われていない宣言を「仮定義」と呼ぶ
- 仮定義は、対応する外部定義が見つかったときはそれと結合され、見つからないときは0で初期化された定義とみなされる
- ファイル有効範囲を持つ識別子に「static」を付けると「内部結合」になり、staticが無い場合は「外部結合」になる
- 内部結合とは翻訳単位の中で行われる結合で、外部結合は翻訳単位を越えて行われる結合
- 関数もオブジェクトと同様のルールで結合される。「extern」は省略可能
- 「インライン関数」はルールが異なる。C99とは異なる仕様のコンパイラーもあるので注意して使うこと
プログラミングライフを楽しく
今回で連載は終了です。連載を通して「Cには確かに学ぶだけの価値がある」と思っていただけたでしょうか。学んだことを活用して、皆さんのプログラミングライフがより楽しくなればと思っています。
最後に、記事を読んでいただいた皆さん、ありがとうございました。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
いまさら聞けないVim(9):ヘッダファイルなどをVimのキー操作一発で探す
前回に続き、Vimが備えるプログラマ向けの便利な機能を紹介します。今回の内容をマスターすれば、プログラミングに関する操作のほとんどをVimから出せるようになります。