情報処理Ⅱ 第8回 2004年11月30日(火) 本日学ぶこと 関数と変数 目的 してはいけないこと 関数を自分で定義し,変数の利用方法・範囲を明示的に制限 することで,適切な機能分割(モジュール化,再利用)を図る. main関数のみで100行以上のプログラム グローバル変数を駆使するプログラム 問題 シーザー暗号ができるか? • ABCDEF...WXYZの各文字を • DEFGHI...ZABCに変換する. Wagahai Zdjdkdl 2 関数(Function) 関数の分類 自作関数: 自分で定義する.⇒ 本日のテーマ ライブラリ関数: 出来合いのもの.printfなど. なぜ関数を定義するのか? 処理を共通化(一般化)する プログラムの見通しをよくする main関数がないとプログラムは動かない 3 関数定義の方法 構文: 型名 関数名(引数並び) {文...} 型名は,関数の戻り値の型.値を返さないときは,voidと書く. 引数並びは,0個以上の「型名 変数名」をカンマで挟んだもの. 引数がないときは,何も書かないか,voidと書く. 例: double myatof(const char *str0) {…} 例: void procedure(int x, int y) {…} 一括の変数宣言と異なり, このintは省略できない. 4 関数の呼び出し 関数定義の例: double succ(double x) {return x+1;} 関数呼び出しの例:b=succ(a); 変数aの値を引数として,関数 succを呼び出し,その戻り値 xを関数succの仮引数(formal を,変数bに格納する. argument),aを関数呼び出しの 実引数(actual argument)と言う.これらを区別する必要 のないときは,ともに引数(argument)という. x = a; の代入を行ってから,関数本文の処理に入る.関数 の処理が終われば,変数x(のオブジェクト)は消滅する. x = succ(x); と書いてもよい.このとき,仮引数のxと,実 引数のxは,別のオブジェクトである. 5 return 関数処理中に return 値; があれば,そこで関数の処理 を終え,値を戻り値(return value)とする. 値の前後にカッコは不要. 戻り値の型がvoidなら, return; と書ける. 6 引数の授受 Cの関数呼び出しでは必ず値渡し(call by value)にな る. 値渡し: 実引数のコピーが仮引数に格納される.その後,仮 引数の値を変更しても,実引数の値には影響しない. 参照渡し(call by reference)をしたければ,ポインタ 値を引数とすればよい. 参照渡し: 仮引数は,実引数の別名となる.その後,仮引数 の値を変更すれば,実引数の値もそれに変わる. Cではこの意味での参照渡しをすることができない. 7 関数定義の順番 呼び出す関数は,呼び出す前に(プログラムファイルの上の ほうで)宣言されていなければならない. 宣言や定義がない場合は,int 関数名(); とみなして呼 び出しを試みる. 対策 呼び出す順序に注意して関数を並べる. 関数プロトタイプを使用する. 8 main関数の型 main関数の(戻り値の)型は,int とする. void main とする本も多いが,規格上適切ではない. 正常終了は return 0; と書き, 異常終了は return 1; と書くのが一般的. 9 変数(Variable)・識別子・オブジェクト 識別子(Identifier): 変数名,関数名,型定義名など の「名前」 オブジェクト(Object)と識別子の違い 識別子は,プログラムファイル(静的)で記述される「ラベル」 オブジェクトは,プログラム実行中(動的)に生成される「実体」 同一の識別子に対して複数のオブジェクトが生成されることも ある. 10 グローバル変数とローカル変数 各変数は,定義された位置によって,有効範囲(scope)を 持つ. グローバル変数 • あらゆるブロックの外で定義された変数. • 有効範囲はファイル末尾まで. ローカル変数 ブロックの中とは, • ブロックの中で定義された変数. { と }で • 有効範囲はブロック終了まで. 挟まれた領域のこと グローバル - global - 大域的 - あらゆるブロックの外 ローカル - local - 局所的 - あるブロックの中 11 識別子の宣言に関するルール グローバルに同一の識別子を複数宣言できない. 一つのブロック内に,同一の識別子を複数宣言できない. 関数はブロック内で定義できない.必ずグローバル区間での 定義となる. ブロック内に変数を定義するときは,それより外にある同一 の識別子と重複してもよい. ブロック内では,外にある同一の識別子は使用できない. モジュール化に関して有用なルール. 12 型の属性 記憶域クラス 型修飾子 extern, static, auto, register const, volatile 例: extern void function1(const char *); int x(void) {static int c=0; ...} 13 記憶域クラス(1) auto: そのオブジェクトの生存期間は自動記憶域期間で ある. static: そのオブジェクトの生存期間は静的記憶域期間 である. 積極的にautoを書くことはない. 必要なときに使う. extern: 他の場所で宣言された識別子を使用する. 分割コンパイルで不可欠. externとstaticは,関数に 対しても指定できる.ただし staticの意味は異なる. プログラムが複数のソースファイルで 構成されることがある.大規模プログ ラミングでは当たり前だが,本授業で は実例を出さない. 14 記憶域クラス(2) 静的記憶域期間 (static変数) staticを指定した場合や,グローバル変数が該当する. プログラムの実行に先立ち,オブジェクトが生成され,初期値が設定 される.プログラム終了まで破棄されない. 初期値を指定しないオブジェクトには 0 が代入される. 初期値は,コンパイル時に計算可能な定数式でなければならない. 自動記憶域期間 (auto変数) autoを指定した場合や,staticやexternの指定なくブロック内で 定義した変数と,関数の仮引数が該当する. 宣言文を実行するたびに,オブジェクトが生成され,初期値があれば 毎回初期化される.ブロックを終えると,破棄される. 初期値を指定しないオブジェクトの初期値は不定. 初期値は任意の計算式でよい. 15 有効範囲?記憶域クラス? ここで問題 グローバルなstatic変数は,定義《 できる ∥ できない 》. グローバルなauto変数は,定義《 できる ∥ できない 》. ローカルなstatic変数は,定義《 できる ∥ できない 》. ローカルなauto変数は,定義《 できる ∥ できない 》. ローカルなstatic変数の用途 ブロック内で情報を保存しておき,あとでまた実行するときに利 用する. 動的に確保することなく,配列領域を戻り値とする. auto変数では扱いきれない大容量オブジェクト(100万個の int配列など)を取り扱う. 16 変数の有効範囲の補足 破棄されるまでは,ブロックの外からでも(ポインタなどで間 接的に)オブジェクトの参照や値の書き換えができる. ⇒ ポインタによる参照渡しが可能となる. 関数内のauto変数に対応するオブジェクトは,関数処理が 行われるたびに生成される. ⇒ 再帰関数が構成できる. 17 型修飾子 const: その識別子の値はプログラムによって変更できな い.参照は可能. volatile: 処理系が知らないうちに値を変える可能性が ある.参照は可能. constの使用例: char *strcpy(char *dest, const char *src); *dest は 関数内実行中 左辺値にできる *src は 関数内実行中 左辺値にできない dest, srcともに 関数内実行中 左辺値にできる 18 暗号化問題 仕様 入力文にあるすべての'A'をα,すべての'B'をβ,…に置き換 えて出力する.(単一換字暗号という.) 文字は,char型の任意の値とする.ただし'\0'を除く('\0' は必ず'\0'に置き換える,と考えてもよい). どの文字をどの文字に置き換えるかは,プログラムの中で指 定する. 簡単な操作で,復号できるようにする. Wagahai Ha Nekodearu • 暗号化 • 復号 Zdjdkdl Kd Qhnrghdux 19 暗号化問題 (まずい)方針 'A'をα,'B'をβ,…に置き換えるのを switch~caseで行う. • プログラムが無駄に長くなる. • 柔軟性に欠ける. char encrypt_word(char c) { switch (c) { case 'A': return 'D'; case 'B': return 'E'; ... } } 20 暗号化問題 方針 暗号化のための変換テーブルを,配列で保持する. • char encrypt_table[256]; encrypt_table['A']の値 encrypt_ table … 'D' +0 'E' +'A' +1 'F' 'G' … +'C' +'B' +'D' +255 21 暗号化問題 方針(続き) 暗号化は,写像を用いる. • a = 'A'; のとき,b = encrypt_table[a]; でbの 値が'D'となるようにするには,あらかじめ encrypt_table['A']の値を'D'としておけばよい. 復号のためのテーブルは,逆写像を用いて求める. • char decrypt_table[256]; • decrypt_table[encrypt_table['A']] = 'A'; gがfの逆写像 であるとは, g(f(x))=x 22 暗号化問題 fが恒等写像 であるとは, f(x)=x 方針(続き) 定義する関数 • 文字列形式の変換テーブルを,配列に変換する. • 復号のためのテーブルの値を設定する. • 変換テーブルを恒等写像にする(変換のリセット). • 変換テーブルを用いて,文字列を暗号化もしくは復号する. 暗号化と復号はどう区別する? • ./encrypt Wakagai Ha Nekodearu • ./encrypt -d Zdjdkdl Kd Qhnrghdux 「コマンドラインオプション」 と呼ばれる 23 まとめ 関数を自分で定義することができる.関数を呼び出すとき, 引数は値渡しで授受される. 変数には,型とは別の属性として,記憶域クラスと型修飾子 を指定できる.static変数とauto変数とでは,大きく挙動 が異なる. 「変数の定義」と「オブジェクトの生成」は別. オブジェクトの生成・破棄のタイミングに注意する. 24
© Copyright 2024 ExpyDoc