【6日でできるC言語入門】複雑なソースコード分割

C言語で本格的なプログラムを作成するとき、ソースコードの機能ごとの分割が不可欠です。
 簡単な分割であれば関数や構造体ごとにファイルを分けるだけで済みますが、データ管理・エラー処理・出力処理などの複数要素が絡み合う場合、依存関係や隠蔽・公開の設計も重要になってきます。
 ここでは「書籍データベース」を題材に、実践的なソースコード分割と各要素の役割・実装方法について詳しく解説します。

1.ソースコード分割の基本

1.1 ファイル分割の目的

目的内容
管理しやすくする1ファイルが長大になるのを防ぎ、見通しを良くする。
機能の独立性変更が他に波及しにくく、保守・修正が簡単
再利用性の向上汎用的な部品として他のプロジェクトでも使える。

1.2 サンプル構成図

  • main.c … プログラム全体の流れ
  • bookDatabase.h/c … 書籍データの管理
  • dataOutput.h/c … データの出力・エラー表示

2.書籍データベース例 ― 分割設計とサンプル

2.1 ヘッダファイルと実装ファイルの役割

ファイル主な内容
bookDatabase.h構造体・定数・列挙型・関数プロトタイプ
bookDatabase.cデータベース管理用の変数・関数実装
dataOutput.h出力・エラー表示の関数プロトタイプ
dataOutput.c出力処理・エラー処理の実装
main.c全体制御、他機能の呼び出し

2.2 サンプルプログラム(書籍データベース)

main.c

プロジェクト/ファイル名: Lesson69_1/main.c

#include <stdio.h>
#include "bookDatabase.h"
#include "dataOutput.h"

int main(void) {
    int i;
    char titles[][TITLE_LENGTH] = {
        "C言語入門", "アルゴリズム図鑑", "データ構造基礎", "Cプログラミング実践"
    };
    char authors[][AUTHOR_LENGTH] = {
        "山田洋", "田中進", "佐藤明", "鈴木理"
    };
    int ids[] = { 101, 102, 102, 103 };

    initDatabase();
    for (i = 0; i < 4; i++) {
        addBook(ids[i], titles[i], authors[i]);
        printf("登録:%d %s (%s)\n", ids[i], titles[i], authors[i]);
        showError();
    }
    for (i = 0; i < 3; i++) {
        showBookData(getBook(i + 101));
    }
    return 0;
}

bookDatabase.h

プロジェクト/ファイル名: Lesson69_1/bookDatabase.h

#ifndef _BOOK_DATABASE_H_
#define _BOOK_DATABASE_H_

#define MAX_BOOK 10
#define TITLE_LENGTH 50
#define AUTHOR_LENGTH 30

typedef struct {
    int id;
    char title[TITLE_LENGTH];
    char author[AUTHOR_LENGTH];
} book;

enum BOOK_ERROR {
    BOOK_OK,
    BOOK_ERROR
};

void initDatabase();
int addBook(int, const char*, const char*);
book* getBook(int);

extern int BookError;

#endif

bookDatabase.c

プロジェクト/ファイル名: Lesson69_1/bookDatabase.c

#include "bookDatabase.h"
#include <string.h>

static int num_of_books = 0;
static book book_database[MAX_BOOK];
int BookError = BOOK_OK;

void initDatabase() {
    int i;
    for (i = 0; i < MAX_BOOK; i++) {
        book_database[i].id = -1;
        strcpy(book_database[i].title, "");
        strcpy(book_database[i].author, "");
    }
    BookError = BOOK_OK;
    num_of_books = 0;
}

int addBook(int id, const char* title, const char* author) {
    if (getBook(id) == NULL && num_of_books < MAX_BOOK) {
        book_database[num_of_books].id = id;
        strncpy(book_database[num_of_books].title, title, TITLE_LENGTH-1);
        strncpy(book_database[num_of_books].author, author, AUTHOR_LENGTH-1);
        num_of_books++;
        BookError = BOOK_OK;
        return 1;
    }
    BookError = BOOK_ERROR;
    return 0;
}

book* getBook(int id) {
    int i;
    for (i = 0; i < num_of_books; i++) {
        if (book_database[i].id == id) {
            return &book_database[i];
        }
    }
    return NULL;
}

dataOutput.h

プロジェクト/ファイル名: Lesson69_1/dataOutput.h

#ifndef _DATA_OUTPUT_H_
#define _DATA_OUTPUT_H_

#include "bookDatabase.h"

void showBookData(book*);
void showError();

#endif

dataOutput.c

プロジェクト/ファイル名: Lesson69_1/dataOutput.c

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

extern int BookError;

void showBookData(book* b) {
    if (b != NULL) {
        printf("書籍ID:%d タイトル:%s 著者:%s\n", b->id, b->title, b->author);
    } else {
        printf("データが登録されていません。\n");
    }
}

void showError() {
    switch (BookError) {
        case BOOK_OK:
            printf("OK!\n");
            break;
        case BOOK_ERROR:
            printf("ERROR!\n");
            break;
    }
}

実行結果

1.「SDLチェック」を「いいえ」にします。

2.メニューの「ビルド」から「ソリューションのビルド」を実行します。

3.プログラムを実行します。

登録:101 C言語入門 (山田洋)
OK!
登録:102 アルゴリズム図鑑 (田中進)
OK!
登録:102 データ構造基礎 (佐藤明)
ERROR!
登録:103 Cプログラミング実践 (鈴木理)
OK!
書籍ID:101 タイトル:C言語入門 著者:山田洋
書籍ID:102 タイトル:アルゴリズム図鑑 著者:田中進
書籍ID:103 タイトル:Cプログラミング実践 著者:鈴木理

3.技術ポイントの整理

3.1 static変数による隠蔽

  • book_databasenum_of_booksstaticで外部から隠し、直接操作されない設計に

3.2 externの使い方

  • エラー状態変数BookErrorはグローバル共有が必要なのでexternで宣言し、エラー処理専用関数で利用

3.3 列挙型の活用

  • enum BOOK_ERRORでエラー状態をグループ化。値と意味を明確に

まとめ

このように、実務的なソースコード分割では、

  • 各機能(データ管理/出力/エラー処理など)ごとにファイル分割
  • staticで変数の隠蔽、externで共有
  • 構造体や定数はヘッダファイルで公開
    というルールを徹底することで、大規模化にも耐える保守性・拡張性・可読性の高い設計ができます。

ぜひこの分割パターンを自分のプロジェクトにも応用してみてください!