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