ミニ補講 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 2024 ExpyDoc