THE UNIVERSITY OF TOKYO 数理情報工学演習第一C プログラミング演習 (第5回 ) 2014/05/12 DEPARTMENT OF MATHEMATICAL INFORMATICS 1 THE UNIVERSITY OF TOKYO 今日の内容: プロトタイプ宣言 ヘッダーファイル,プログラムの分割 双方向リスト 課題:リスト,疎行列 2 THE UNIVERSITY OF TOKYO プロトタイプ宣言 • C言語では,関数や変数は使用する前(ソースの上のほう)に 定義されている必要がある. double sub(int x, double y) { return (double)x * y; } int main() { double z; z = sub(1, 2.5); // OK } 3 int main() { int z; z = sub(1, 2.5); // NG } double sub(int x, double y) { return (double)x * y; } THE UNIVERSITY OF TOKYO 関数のプロトタイプ宣言 double sub(int x, double y); // 後 (下) に出てくる関数の型を示す int main() { double z; z = sub(1, 2.5); // OK } double sub(int x, double y) { return (double)x * y; } 4 THE UNIVERSITY OF TOKYO ヘッダーファイル • (標準)ライブラリに入っている関数や定数が宣言してある #include <stdio.h> // 標準入出力ヘッダー int main() { printf(“Hello.∖n”); // stdio.h 内で宣言してある } 5 THE UNIVERSITY OF TOKYO stdio.h で宣言してあるもの • FILE構造体 • fopen, fclose, fprintf, fscanf, … • NULL • EOF 6 THE UNIVERSITY OF TOKYO stdlib.h で宣言してあるもの • malloc, free • exit • atoi, atof • rand (乱数発生) • qsort (ソート) • bsearch (2分探索) 7 THE UNIVERSITY OF TOKYO プログラムの分割 • 1つのファイルに全ての処理を書くと – 長くて分かりにくい – プログラムの再利用がしにくい – (再コンパイルに時間がかかる) • まとまった処理ごとに分割してファイルを作る – 行列演算 (matrix.c) – 全体 (main.c) – gcc main.c matrix.c • 注: それぞれのファイルは単独でコンパイルできるようにする ⇒ヘッダーファイルを使う 8 THE UNIVERSITY OF TOKYO matrix.h の中身 typedef struct { int n,m; slist **R; } smatrix; smatrix *smatrix_read(char *filename); void smatrix_write(char *filename, smatrix *a); smatrix *smatrix_product(smatrix *A, smatrix *B); 9 THE UNIVERSITY OF TOKYO matrix.c の中身 #include “matrix.h” // 構造体の定義を読み込む 自分で作ったファイルを読み込む ときは “…” で括る smatrix *smatrix_read(char *filename) { // 関数の中身を書く } void smatrix_write(char *filename, smatrix *a) { // } 10 THE UNIVERSITY OF TOKYO main.c の中身 #include “matrix.h” // 構造体の定義や関数のプロトタイプ宣言 int main(int argc, char *argv[]) { smatrix *A, *B, *C; // smatrixは matrix.h で定義してある A = smatrix_read(argv[1]); B = smatrix_read(argv[2]); C = smatrix_product(A, B); smatrix_write(argv[3], C); } 11 THE UNIVERSITY OF TOKYO 分割コンパイル • 各ソースファイルを個別にコンパイルし,最後に実行ファイルを作る – gcc –c –o matrix.o matrix.c –O3 #matrix.cのコンパイル – gcc –c –o main.o main.c –O3 #main.cのコンパイル – gcc –o main.out matrix.o main.o #リンク • 修正したファイルだけ再コンパイルできる • 注:ヘッダーファイルを修正した場合,それをインクルードしている全ての ソースファイルは再コンパイルする必要がある • 自動化するには make コマンドを使用する 12 THE UNIVERSITY OF TOKYO 2重インクルードの防止 • ヘッダーファイル中で別のヘッダーファイルをインクルードする場合などに, 構造体等の宣言が2回出てくるとエラーになる • 2重インクルードを防止するために,ヘッダーファイル内にフラグを用意する #ifndef MATRIX_H // MATRIX_Hが定義されていなければ以下が読まれる #define MATRIX_H // 1回目に読まれたときに MATRIX_H を定義する // matrix.h の中身を書く … #endif // #ifndef の行と対応 13 THE UNIVERSITY OF TOKYO Tips • ヘッダファイルの中で構造体を宣言する場合,その中で他の構造体を 使っているとそれの宣言も必要になる • ヘッダファイルではプロトタイプ宣言だけにしておく • 構造体内部へのアクセスは matrix.c 内のみで行うようにする // matrix.h // matrix.h typedef struct smatrix_{ int n,m; typedef struct smatrix_ smatrix; smatrix *smatrix_read(char *); slist **R; } smatrix; smatrix *smatrix_read(char *); 14 THE UNIVERSITY OF TOKYO Tips • 全ての関数は外部のオブジェクトファイル (.o) から参照できる • 下手に名前をつけると,名前の衝突が起きてしまう • 外部で使用しない関数には static をつける // matrix.c static sub(smatrix *A) {…} // matrix.c の外からは見えない smatrix *smatrix_transpose(smatrix *A) // 外から見える { smatrix *B; B = sub(A); // matrix.c 内ではsubは使える } 15 THE UNIVERSITY OF TOKYO 連結リスト (Linked Lists) オブジェクトをある線形順序に並べて格納するデータ構造 単方向連結リスト (singly linked list) L の要素 x キーフィールド key ポインタフィールド next x->next: リスト中の x の直後の要素のポインタ x->next = NIL のとき,x は最後の要素 L->head: リストの先頭の要素のポインタ L->head = NIL のとき,リストは空 key L 16 9 16 next NIL 4 THE UNIVERSITY OF TOKYO リストの種類 一方向 (singly linked) と双方向 (doubly linked) 一方向のとき,各要素は prev を持たない 既ソート (sorted) と未ソート 既ソート: リストの線形順序はキーの線形順序に対応 未ソート: 任意の順序 循環 (circular list) と非循環 循環: リストの先頭要素の prev はリストの末尾を指し,末尾の next はリスト の先頭を指す 17 THE UNIVERSITY OF TOKYO 双方向リストの構造体 リストの要素 typedef struct dlobj_ { struct dlobj_ *prev; // 前の要素へのポインタ struct dlobj_ *next; // 後の要素へのポインタ data key; // キー } dlobj; 双方向リスト typedef struct { dlobj *head; } dlist; 18 // 先頭要素のポインタ THE UNIVERSITY OF TOKYO 連結リストの探索 dlist_search(L, k): リスト L に属する,キー k を持つ最初の要素のポインタを返す キー k を持つ要素が存在しなければ NULL を返す (n) 時間 head(L) 19 dlobj *dlist_search(dlist *L, data k) { dlobj *x; x = L->head; while (x != NULL && x->key != k) { x = x->next; } return x; } 9 16 4 THE UNIVERSITY OF TOKYO 連結リストへの挿入 dlist_insert(L, x): x を L の先頭に挿入 x のキーは既にセットされているとする O(1) 時間 x void dlist_insert(dlist *L, dlobj *x) { x->next = L->head; if (L->head != NIL) L->head->prev = x; L->head = x; x->prev = NIL; } 9 head(L) head(L) 20 9 16 4 16 4 THE UNIVERSITY OF TOKYO 連結リストからの削除 dlist_delete(L, x): L から x を削除 O(1) 時間 void dlist_delete(dlist *L, dlobj *x) { if (x->prev != NULL) x->prev->next = x->next; else L->head = x->next; if (x->next != NIL) x->next->prev = x->prev; } 21 head(L) 9 head(L) 9 x 16 4 4 THE UNIVERSITY OF TOKYO 双方向循環リストの構造体 リストの要素 (以前と同じ) typedef struct dlobj_ { struct dlobj_ *prev; // 前の要素へのポインタ struct dlobj_ *next; // 後の要素へのポインタ data key; // キー } dlobj; 双方向リスト typedef struct { dlobj *nil; } dlist; 22 // NILを表す要素のポインタ THE UNIVERSITY OF TOKYO データ構造の変更点 リスト L は NIL を表現するオブジェクトを持つ 双方向リストをNILを含めた循環リストに変更 先頭要素は L->nil->next nil(L) 9 16 4 NIL 23 THE UNIVERSITY OF TOKYO 連結リストの探索 dlist_search(L, k): リスト L に属する,キー k を持つ最初の要素のポインタを返す キー k を持つ要素が存在しなければ NIL を返す dlobj *dlist_search(dlist *L, data k) (n) 時間 { dlobj *x; x = L->nil->next; while (x != L->nil && x->key != k) { x = x->next; } nil(L) return x; } 9 24 16 4 THE UNIVERSITY OF TOKYO 連結リストへの挿入 dlist_insert(L, x): x を L の先頭に挿入 void dlist_insert(dlist *L, dlobj *x) { x のキーは既にセットされているとする x->next = L->nil->next; L->nil->next->prev = x; L->nil->next = x; x->prev = L->nil; O(1) 時間 x nil(L) 9 9 25 } 16 4 16 4 THE UNIVERSITY OF TOKYO 課題(初級): 双方向循環リスト x を含むリストの要素を作成 dlobj *dlobj_new(data x); 空のリストの作成 dlist *dlist_new(void); リストから,k を含む要素を探索 dlobj *dlist_search(dlist *L, data k) リストの先頭に r を挿入 void dlist_insert(dlist *L, dlobj *r) リストの末尾に r を追加 void dlist_append(dlist *L, dlobj *r) 26 THE UNIVERSITY OF TOKYO リストから,r を削除 void dlist_delete(dlist *L, dlobj *r) r のメモリは解放しない リスト全体を消去 void dlist_free(dlist *L) リスト自体と,その中の全要素のメモリを解放 (free) する • dlist.h, dlist.c, main.c に分割する • 初級:明日(5/13)の正午までに提出 • 宛先:[email protected] 27 THE UNIVERSITY OF TOKYO 課題(中級) 双方向循環リストを用いて疎行列を作る 疎行列をファイルから読み込み,新たに確保した疎行列に代入 smatrix *smatrix_read(char *filename); 疎行列を入力と同じ形式でファイルに出力 void smatrix_write(char *filename, smatrix *a); 2つの疎行列 A, B の積を計算し,新たに確保した疎行列 C に代入 smatrix *smatrix_product(smatrix *A, smatrix *B); リストの途中への追加は遅いので,先頭または末尾への追加のみ用いる 積の計算も,(i,j) 要素へのaccessを用いると遅いので,リストを先頭から見て いく操作のみで実現する 28 THE UNIVERSITY OF TOKYO • dlist.h, dlist.c, matrix.h, matrix.c, main.c に分割する • 中級:5/16(金) の正午までに提出 • 宛先:[email protected] 29 THE UNIVERSITY OF TOKYO
© Copyright 2024 ExpyDoc