情報処理Ⅱ 2006年12月15日(金)
前回の補足1 ライブラリ関数の分類 関数・定数・独自型などをまとめて,他のプログラムから利用できるよう部品化したものを「ライブラリ」という. 標準ライブラリ関数(標準関数,ANSI準拠の関数) この科目で使用するのは,この範囲だけ POSIX準拠の関数 情報ネットワーク演習で使用する その他(サードパーティライブラリ) OpenGL(ビジュアル情報演習で使用する) 関数・定数・独自型などをまとめて,他のプログラムから利用できるよう部品化したものを「ライブラリ」という. http://ja.wikipedia.org/wiki/POSIX ANSI: American National Standard Institute POSIX: Portable Operating System Interface for UNIX
前回の補足2 複雑な式も,木構造(構文木)で表現できる. コンパイラは,ソースコード(文字列)をこの種の木構造で表現し,それから計算機が実行できるコード(オブジェクトコード)に変換する.処理の中で,再帰呼び出しが用いられることがある. 「s != NULL && *s != '\0'」の 構文木 && != != s NULL * '\0' 「構文木」は「こうぶんぎ」という. 構文木と再帰の関係について興味がある人は,「再帰的下向き構文解析法」を自習してほしい. s
自分の思う通りに,適切な形で,プログラムとして表現する. 授業の進め方 構造体 その他の型 2年以降で さらに学習・習熟 プリプロセッサ 指令 ライブラリ関数 ファイル入出力 自分の思う通りに,適切な形で,プログラムとして表現する. 配列・文字列 ポインタ 関数 変数の 有効範囲 識別子 算術型 再帰呼び出し 制御文 演算子 プログラムの作成・ コンパイル・実行
本日学ぶこと いくつかの型 なぜ「型」にこだわるか? 以下を読む上での注意 typedef 列挙型(enum型) 構造体 処理対象のデータを効果的・効率的に取り扱える 既存の機能をもとに,便利な機能を創れる 以下を読む上での注意 この色とこの色は型名を表す. この色は変数名を表す. プログラムを書きやすく 読みやすくする…定性的なメリット 実行時のコスト(メモリ使用量など)を見積もることができ,そこからプログラムの改善が期待できる…定量的なメリット
typedef 独自に型(type)を定義(define)できる. 例 構造体の型名定義によく用いられる. typedef signed char schar; schar c; ⇒ signed char c; と同じ typedef unsigned char uchar; uchar c, *p; ⇒ unsigned char c, *p; と同じ typedef char *String; String s, t; ⇒ char *s, *t; と同じ typedef char c5[5]; c5 x, y, z; ⇒ char x[5], y[5], z[5]; と同じ 構造体の型名定義によく用いられる.
typedefの考え方 変数の定義 型の定義 char c5[5]; は,変数c5を定義し,その型はchar [5]型とする.
列挙型(enumeration)とは 「ラベル」を格納・参照・比較したいときに使う. 構文: enum タグ名 { 列挙子並び }; 変数に格納される具体的な値には関心がない. 実際には,ラベルに整数値が割り当てられる.これを積極的に活用することもある. 構文: enum タグ名 { 列挙子並び }; 「enum タグ名」型が定義される. 列挙子は「,」で区切る.
列挙型の使用例 1 例 enum Boolean {FALSE, TRUE}; enum Boolean p = TRUE, q = FALSE, r = p||q; enum month { Jan = 1, Feb, Mar, ..., Dec, MONTH_END } mloop; int rain[100][MONTH_END]; for (mloop = Jan; mloop < MONTH_END; mloop++) { rain[6][mloop] = ...; } 13 型と変数を同時に定義 年月単位の降水量を格納する
汎整数型 整数型(char,int,long など)と列挙型を合わせて, 汎整数型(integral type)という. ただし,利用は代入,比較,範囲内の増分・減分にとどめておく.
構造体(structure) 複数のオブジェクトを取りまとめて一つのオブジェクトとして扱うための機構 構造体で表現するとよいもの (2次元,3次元などの)座標上の点 複素数 行列(行数と列数と各成分) 所持金 学生情報 (x,y) 500 50 100 10 学生番号:0001 氏名:あいうえお 情処Ⅰの点数:80 情処Ⅱの点数:75 修得単位数: 52
構造体の構文と定義の例(1) 構文: struct タグ名 { メンバ宣言子並び }; 例 「struct タグ名」型が定義される. struct point { double x; double y; }; struct point p = {1, -1}; セミコロンを忘れないように この x と y を, point構造体の メンバという. x = 1 y = -1 p: 配列と同じ書式で 初期化可能
構造体の定義の例(2) 構造体宣言と変数定義を一つの文で 構造体宣言と型定義を一つの文で struct point { double x; double y; } p; 構造体宣言と型定義を一つの文で typedef struct point { double x, y; } point; point p; 構造体宣言と型定義とを同時に行った場合, struct point型とpoint型とで相互に参照・代入してよい. struct point型と point型が定義される
構造体の定義の例(3) 無名構造体 struct { double x; double y; } p; typedef struct { double x, y; } point; point p; 無名構造体として定義した変数は,そのメンバへのアクセスはできるが, 関数呼び出しの引数としては使用できない(どんな型か,コンパイラは分からない). typedef struct {略} point; と定義した場合,その型のオブジェクト同士では参照や代入ができるが, 前のページのstruct point型と相互に(構造体として)代入することは,できない.
構造体の定義の例(4) 行列(行数,列数とも10まで) 行列(成分はポインタ) struct matrix { row column int row, column; double element[10][10]; }; 行列(成分はポインタ) double **element; row column element … row column element 上の構造体について, rowとcolumnは,コンパイラが参照するためのラベルであると同時に, この構造体のオブジェクトを定義すると,それぞれの値を格納する領域もメモリ上に確保される. elementは,コンパイラが参照するためのラベルであるが,メモリ上には確保されない. elementが参照する二次元配列領域は,メモリ上に確保されるが,elementを介することなく, 直接各要素の値を獲得したり,書き換えたりすることはできない. 下の構造体について,行列の各要素は,構造体オブジェクトの外で確保することになる.
構造体の定義の例(5) 自己参照構造体 struct node { graph … value = 1 value = 99 next int value; struct node *next; }; struct node graph[100]; graph[0].value = 1; graph[0].next = graph + 1; graph[1].value = 99; graph[1].next = NULL; graph … value = 1 value = 99 next next = NULL 自己参照構造体が定義できるのは, メンバnextの型が(自己参照する)構造体そのものではなくそのポインタだから. 専門用語を用いると,nextの型を決定する時点では「struct node」型が「不完全型」として利用可能となっていて, 型定義の「};」を終えたところで,「struct node」型は「構造体型」とみなされる. そもそも,どのようにメモリに構造体オブジェクトを作ればよいかを考えると, メンバが,自己参照する構造体そのものではまずい(その型が定義できて, 変数も確保できたとして,sizeof(変数)が計算できる?). ここで問題.無名構造体かつ自己参照構造体という型を作ることはできるか?
構造体の参照と代入(1) 前提 メンバへのアクセス方法 struct point { double x, y; }; struct point p, *pp = &p; メンバへのアクセス方法 構造体オブジェクトpのメンバxは p.x と書いて 参照・代入ができる. 構造体オブジェクトのポインタppについては,(*pp).x もしくは pp->x と書けばよい. . と -> は演算子であり,最上位の優先順位を持つ. x y p: pp × *pp.x
構造体の参照と代入(2) 構造体オブジェクトの代入 構造体渡し struct matrix a, b; (aを初期化) b = a; とすると,bにはaのコピーが格納される. 構造体の中に配列があっても,コピーされる. 構造体の中のポインタは,ポインタ値がコピーされる. 構造体渡し 関数の引数や戻り値の授受でも,構造体オブジェクトが代入(コピー)される. しかし,構造体はしばしばポインタで渡される(参照渡し). 関数処理で,構造体オブジェクトの中の値を変えたいとき 大きなサイズの構造体オブジェクトをスタックに置きたくないとき
両替問題の仕様(1) 両替機の両替部分をシミュレート(模擬)する. 入金に対して,どの紙幣・貨幣を何枚出せばいいかを出力する. 「崩す」(一万円札を千円札10枚にするなど)のは考えない. 金額に関する情報 一万円,五千円,千円の各紙幣(bill)の枚数 500円,100円,50円,10円,5円,1円の各硬貨(coin)の枚数 合計金額(amount) ⇒ 構造体のメンバ? 100 100 100 100 500 50 10 10 10 5 5 5 5
両替問題の考え方 知りたい情報,行いたい操作 紙幣・貨幣がそれぞれ何枚ある(入金された,出金しないといけない)かを知る 合計金額を求める 両替する 両替前の「紙幣・貨幣の枚数の情報」に対して, 両替後の「紙幣・貨幣の枚数の情報」を求める
両替問題のプログラミング(1) 金銭構造体 struct money { int bill_10000, bill_5000, bill_1000; int coin_500, coin_100, coin_50, coin_10, coin_5, coin_1; };
両替問題のプログラミング(2) 定義した関数 void print_money(struct money *moneyp); 枚数と総額を出力する 構造体のポインタを引数にとる int calc_amount(struct money *moneyp); 総額を求める struct money change_money(struct money money_in); 両替する 構造体オブジェクトを引数にとり,構造体オブジェクトを返す 「calc」は,「calculate (計算する)」という単語による.
その他の話題 構造体と配列の使い分け sizeof(構造体) 構造体とプログラミング してはいけないこと 独自の型はどこで定義する?
構造体と配列の使い分け 配列 構造体 「配列を含む構造体」や「構造体の配列」もよく利用される. 同一の型の集まり 各要素に番号がついている ループなどを使って順番に処理する 構造体 様々な型の集まり(ただし,同一の型の集まりでもよい) 各要素に付けられた名前を使ってアクセスする 「配列を含む構造体」や「構造体の配列」もよく利用される.
sizeof(構造体) sizeof(構造体) は,構造体の各メンバのサイズの合計以上になる. 構造体オブジェクトの中で使用されない領域もあり得る.「パディング」という. s: c i パディング struct s { char c; int i; }; sizeof(char) == 1 sizeof(int) == 4 でも sizeof(struct s) != 5
構造体とプログラミング さまざまな種類の情報を持つ「実体」を一つのオブジェクトとして処理したいとき, まず,「何に処理をしたいか?」を考える. そのオブジェクトの型を構造体で定義する. 座標上の点,学生情報,ノード(節点),など. 次に,「何の処理をしたいか?」を考える. 構造体を引数にとって処理する関数を定義する. 引数・戻り値ともに,型は構造体そのものにすべきか, 構造体のポインタにすべきか,そのつど検討する.
してはいけないこと × 関数内のauto変数を指し示すポインタを,戻り値とする. struct money *create_money(void) { struct pocket money = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return &money; } コンパイルエラーにはならないが,実行時に支障をきたす. 関数処理が終わると,moneyの領域が破棄される ⇒ *(&money)が不定!
独自の型はどこで定義する? 一般には,グローバル区間で定義し,複数の関数間で利用できるようにする. ブロックの中で定義してもよい. #include文の後,関数プロトタイプの前に書く. ブロックの中で定義してもよい. 有効範囲は,そのブロックの終わりまで.
まとめ 独自の型(typedef,列挙型,構造体)を定義することで,変数に持たせる意味がより明確になる. 構造体により,複数のデータからなる「システム」を創ることができる. 構造体を対象とする関数を定義するとき, (引数・戻り値の)型は構造体そのものにすべきか,構造体のポインタにすべきか,そのつど検討する.
予告 次回授業終了後すぐ,授業Webページにて,第2回レポート課題を掲示します.