ミニ補講
Cプログラムのモジュール化
2014/6/6
山本・Cuturi研究室 M1
山口 慧
内容
モジュール化とは
C言語でのモジュール化の方法
ここが重要
ヘッダファイルの使い方
修飾子extern
修飾子static
インクルードガード
補足
宣言と定義について
コードを分割したときの大域変数について
モジュール化とは
一般に一つの大きなシステムを独立した機能に分割して,
それぞれ別々に設計や管理を行うこと
例:CPUのレジスタ, ALU, プログラムカウンタ…
各機能の外部仕様を決めておくことで, 各機能の内部の管
理, 改良, デバッグが行いやすくなる
例:実験3のHWで外部仕様がわかっていれば, 隣の人に
作ってもらったモジュールも使うことができた(はず)
プログラミングでのモジュール化
ライブラリも機能によって分割されたモジュール
関数などがどのように実装されているかはわからなくて
も, 仕様(引数や返り値について)を理解していれば使うこ
とができる
例: C++のmapという連想配列は赤黒木で実装されてい
る(らしい)が, 木の探索法や挿入法を知らなくても使
うことができる
連想配列:int以外をインデックスにできるリストのようなもの
Cでのモジュール化の方法
ヘッダファイル(.h)を使う
コンパイル時にまとめて書くgcc main.c hello.c
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
ヘッダファイル(.h)を使う
構造体の定義や関数のプロトタイプ宣言だけを書く
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
#includeすると指定したファイルがその場に展開される
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
関数の中身はソースファイル(.c)に書く
externは他のファイルに中身が書いてあることを示す
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
ヘッダで宣言されていないhoge()はmain.cで使えない?
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
ヘッダで宣言されていないhoge()はmain.cで使えない?
実はexternをつけて宣言しなくてもgccが勝手に他の
ソースファイルからhoge()を見つけてきてくれる
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
ヘッダで宣言されていないhoge()はmain.cで使えない?
実はexternをつけて宣言しなくてもgccが勝手に他の
ソースファイルからhoge()を見つけてきてくれる
ただしintを返す関数だと解釈される
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
異なるファイルでも同じ名前の関数を定義していると
エラーが出る
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
void hoge(){
……
}
#endif
Cでのモジュール化の方法
staticという修飾子をつけると絶対他のファイルから呼
び出すことはできない
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
static void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
static void hoge(){
……
}
#endif
Cでのモジュール化の方法
#ifndef, #define, #endifはプリプロセッサと呼ばれるもの
#includeもプリプロセッサ
コンパイル前にファイルに対して前処理を行う
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
static void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
Cでのモジュール化の方法
#ifndef, #define, #endifはプリプロセッサと呼ばれるもの
同じヘッダを二回#includeすることを防止
同じ型が二回定義されているというエラーを回避
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
static void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
インクルードガード
#ifndef, #define, #endifはプリプロセッサと呼ばれるもの
同じヘッダを二回#includeすることを防止
同じ型が二回定義されているというエラーを回避
hello.h
_HELLO_を定義する
#ifndef _HELLO_
#define _HELLO_
typedef struct name{
char* str;
}name
extern void hello(name);
#endif
もし_HELLO_が定義され
ていなければ#endifまでの
コードを残す
モジュール化のポイント
書き始める前に, 大体どのようなモジュール化ができそう
か考える
初めから意識していないと, 後から分割するのは面倒
モジュール(機能)ごとにファイルを分ける
内部実装が外から見えないようにする
外から使える関数の取捨選択
staticをつけた関数は外から使えない
まとめ
プログラムを機能で分割することをモジュール化という
デバッグや改良のしやすさ, 可読性が向上する
ヘッダファイルには構造体の定義や関数のプロトタイプのみ
を書く
ヘッダファイルにはインクルードガードを書いておく
関数の宣言にはexternをつけておく
キーワード:モジュール化, ソース分割, ヘッダファイル, プ
リプロセッサ, 実装の隠蔽, など
これらのキーワードで検索するともっと詳しく説明されて
ます
補足
宣言と定義
C言語において関数(変数)の宣言(declaration)とは, 宣言し
た型と名前を持つ関数(変数)が存在することをコンパイ
ラに教えること
変数の定義は変数の中身を保存するメモリ領域を確保さ
せること
関数の定義は実際に行う処理を書いたもの
宣言と定義
C言語において関数(変数)の宣言(declaration)とは, 宣言し
た型と名前を持つ関数(変数)が存在することをコンパイ
ラに教えること
変数の定義は変数の中身を保存するメモリ領域を確保す
ること
関数の定義は実際に行う処理を書いたもの
定義があればそこで宣言も行う
修飾子externは宣言を行うためのもの
宣言は何回やってもよいが定義は一度だけ
main.cでの宣言と定義
まずhello.hが#includeされているのでvoid hello()が宣言される
ここでコンパイラは引数がnameで返り値がvoidの関数helloが
あることがわかる
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
static void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
main.cでの宣言と定義
name n; では宣言と定義がどちらも行われる
宣言:nameという型の変数nがあることがわかる
定義:変数n にメモリを割り当てる
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
printf(“hello, %s.¥n”, n.str);
}
static void hoge(){
……
}
int main(){
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
変数の宣言
変数も宣言だけを行うことができる
修飾子externを使う
変数の宣言
変数も宣言だけを行うことができる
修飾子externを使う
どこかに定義をする必要がある
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
extern int i;
#include <stdio.h>
#include “hello.h”
int i=10;
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
int j;
for(j=0; j<i; j++){
printf(“hello, %s.¥n”, n.str);
}
}
int main(){
i=3;
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
変数の宣言
これでもうまく動く
main.c
hello.h
hello.c
#include “hello.h”
int i;
#ifndef _HELLO_
#define _HELLO_
#include <stdio.h>
#include “hello.h”
int i=10;
int main(){
i=3;
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
typedef struct name{
char* str;
}name;
extern void hello(name);
#endif
void hello(name n){
itn j;
for(j=0; j<i; j++){
printf(“hello, %s.¥n”, n.str);
}
}
変数の宣言
変数にもstaticをつけると他のファイルからは参照できない
この場合はエラーが出る
main.c
hello.h
hello.c
#include “hello.h”
#ifndef _HELLO_
#define _HELLO_
extern int i;
#include <stdio.h>
#include “hello.h”
static int i=10;
typedef struct name{
char* str;
}name;
extern void hello(name);
void hello(name n){
itn j;
for(j=0; j<i; j++){
printf(“hello, %s.¥n”, n.str);
}
}
int main(){
i=3;
name n;
n.str = “Satoru”;
hello(n);
return 0;
}
#endif
大域変数にはstaticをつけておく
基本的にファイル間で大域変数を共有するのはバグの元
意図せず同じ名前の大域変数を別のファイルで使ってしま
うと共有されてしまう
うまく扱うためにはファイルの内部がよくわかっていない
といけないのでモジュール化の観点からもダメ
どうしても他のファイルからあるファイルの大域変数を操作
したいなら, 値を操作する関数を作る
get_[変数名]()やset_[変数名]([型] value)など
局所変数にstaticをつけたときの動作は異なるので調べてください
まとめ
変数や関数は使う前に宣言をしておく必要がある
また、必ずどこかに定義が必要
定義は1回だけ
修飾子externは宣言を行うためのもの
この場合異なるファイルに定義があってもよい
他のファイルから見えてほしくない大域変数や関数には
修飾子staticをつける
© Copyright 2026 ExpyDoc