情報処理Ⅱ 2005年12月2日(金) 本日学ぶこと 関数と変数 目的 してはいけないこと 関数を自分で定義し,変数の利用方法・範囲を明示的に制限 することで,適切な機能分割(モジュール化,再利用)を図る. main関数のみで100行以上のプログラム グローバル変数を駆使するプログラム 問題 並べ替えられる? 4 7 3 8 6 1 2 5 1 2 3 4 5 6 7 8 2 関数(Function) 関数の分類 自作関数: 自分で定義する.⇒ 本日のテーマ ライブラリ関数: 出来合いのもの.printfなど. なぜ関数を定義するのか? 処理を共通化(一般化)する プログラムの見通しをよくする main関数がないとプログラムは動かない 3 関数定義の方法 構文: 型名 関数名(引数並び) {文...} 型名は,関数の戻り値の型.値を返さないときは,voidと書く. 引数並びは,0個以上の「型名 変数名」をカンマで挟んだもの. 引数がないときは,何も書かないか,voidと書く. 例: double myatof(const char *str0) {…} 例: void procedure(int x, int y) {…} 一括の変数宣言と異なり, このintは省略できない. 4 関数の呼び出し 関数定義の例: double f(double x) {return x+1;} 関数呼び出しの例:b=f(a); 変数aの値を引数として, 関数fを呼び出し,その戻 x を関数 f の仮引数(formal り値を,変数bに代入する. argument),a を関数 f の 実引数(actual argument)と言う.これらを区別する必要 のないときは,ともに引数(argument)という. x = a; の代入を行ってから,関数本文の処理に入る.関数 の処理が終われば,変数 x (のオブジェクト)は消滅する. x = f(x); と書いてもよい.このとき,仮引数の x と,実 引数の x は,別のオブジェクトである. 5 return 関数処理中に return 値; があれば,そこで関数の処理 を終え,値を戻り値(return value)とする. 値の前後にカッコは不要. 戻り値の型がvoidなら, return; と書ける. 6 引数の授受 Cの関数呼び出しでは必ず値渡し(call by value)にな る. 値渡し: 実引数のコピーが仮引数に格納される.その後,仮 引数の値を変更しても,実引数の値には影響しない. 参照渡し(call by reference)をしたければ,ポインタ 値を引数とすればよい. 参照渡し: 仮引数の値を変更すれば,実引数の値もそれに変 わる.Cではこの意味での参照渡しをすることができないが,ポ インタ値を渡すことで,その参照先の値を変えることができる. • ポインタ値とは…配列変数の名前,または変数に「&」をつけ たもの 7 ポインタ値の値渡し 関数: void print_message(char *message) { printf("%s\n", message); 実引数の値(ポイン } 呼び出し側: char wakayama[] = "wakayama"; print_message(wakayama); message wakayama タ値)は変更不可. 参照先(文字列 "Wakayama"の 中身)は変更可. 関数print_messageの中 'W' 'a' 'k' 'a' 'y' 'a' 'm' 'a' '\0' 8 main関数の型 main関数の(戻り値の)型は,int とする. void main とする本も多いが,規格上適切ではない. 正常終了は return 0; と書き, 異常終了は return 1; と書くのが一般的. 9 関数定義の順番 呼び出す関数は,呼び出す前に(プログラムファイルの上の ほうで)宣言されていなければならない. 宣言や定義がない場合は,int 関数名(); とみなして呼 び出しを試みる. 対策 呼び出す順序に注意して関数を並べる. 関数プロトタイプを使用する. 10 関数プロトタイプ (Function prototype) 構文: 型名 関数名(引数の型の並び); 「関数原型」 ともいう セミコロンを忘れずに 「引数の型の並び」は,「引数(型と変数)の並び」でもよい.こ のとき変数名は無視される. 一般に,グローバル区間に記述する. 例: int swapcase(int); 関数プロトタイプを用いることで, 関数定義の順番を気にすることなくプログラムを記述できる. • ただし,関数プロトタイプは,関数定義の前に書くこと. 関数の入出力が明確になる. 11 変数(Variable)・識別子・オブジェクト 識別子(Identifier): 変数名,関数名,型定義名など の「名前」 オブジェクト(Object)と識別子の違い 識別子は,プログラムファイル(静的)で記述される「ラベル」 オブジェクトは,プログラム実行中(動的)に生成される「実体」 同一の識別子に対して複数のオブジェクトが生成されることも ある. 12 グローバル変数とローカル変数 各変数は,定義された位置によって,有効範囲(scope)を 持つ. グローバル変数 • あらゆるブロックの外で定義された変数. • 有効範囲はファイル末尾まで. 「ブロックの中」とは, ローカル変数 { と }で 挟まれた領域のこと • ブロックの中で定義された変数. • 有効範囲はブロック終了まで. 関数の仮引数も ローカル変数 グローバル - global - 大域的 - あらゆるブロックの外 ローカル - local - 局所的 - あるブロックの中 13 識別子の宣言に関するルール グローバルに同一の識別子を複数宣言できない. 一つのブロック内に,同一の識別子を複数宣言できない. 関数はブロック内で定義できない.必ずグローバル区間での 定義となる. ブロック内に変数を定義するときは,それより外にある同一 の識別子と重複してもよい. ブロック内では,外にある同一の識別子は使用できない. モジュール化に関して有用なルール. 14 型の属性 記憶域クラス 型修飾子 extern, static, auto, register const, volatile 例: extern void function1(const char *); int x(void) {static int c=0; ...} 15 記憶域クラス(1) auto: そのオブジェクトの生存期間は自動記憶域期間で ある. static: そのオブジェクトの生存期間は静的記憶域期間 である. 積極的にautoを書くことはない. 必要なときに使う. extern: 他の場所で宣言された識別子を使用する. 分割コンパイルで不可欠. externとstaticは,関数に 対しても指定できる.ただし staticの意味は異なる. プログラムが複数のソースファイルで 構成されることがある.大規模プログ ラミングでは当たり前だが,本授業で は実例を出さない. 16 記憶域クラス(2) 静的記憶域期間 (static変数) staticを指定した場合や,グローバル変数が該当する. プログラムの実行に先立ち,オブジェクトが生成され,初期値が設定 される.プログラム終了まで破棄されない. 初期値を指定しないオブジェクトには 0 が代入される. 初期値は,コンパイル時に計算可能な定数式でなければならない. 自動記憶域期間 (auto変数) autoを指定した場合や,staticやexternの指定なくブロック内で 定義した変数と,関数の仮引数が該当する. 宣言文を実行するたびに,オブジェクトが生成され,初期値があれば 毎回初期化される.ブロックを終えると,破棄される. 初期値を指定しないオブジェクトの初期値は不定. 初期値は任意の計算式でよい. 17 有効範囲?記憶域クラス? ここで問題 グローバルなstatic変数は,定義《 できる ∥ できない 》. グローバルなauto変数は,定義《 できる ∥ できない 》. ローカルなstatic変数は,定義《 できる ∥ できない 》. ローカルなauto変数は,定義《 できる ∥ できない 》. ローカルなstatic変数の用途 ブロック内で情報を保存しておき,あとでまた実行するときに利 用する. 動的に確保することなく,配列領域を戻り値とする. auto変数では扱いきれない大容量オブジェクト(100万個の int配列など)を取り扱う. 18 配列のauto変数 関数: void print_message(void) { char message[] = "Wakayama"; printf("%s\n", message); } 関数print_messageの中 message 'W' 'a' 'k' 'a' 'y' 'a' 'm' 'a' '\0' 19 変数の有効範囲の補足 破棄されるまでは,ブロックの外からでも(ポインタなどで間 接的に)オブジェクトの参照や値の書き換えができる. ⇒ ポインタによる参照渡しが可能となる. 関数内のauto変数に対応するオブジェクトは,関数処理が 行われるたびに生成される. ⇒ 再帰関数が構成できる. 関数print_messageの中 message wakayama message[0] = 'w'; としてよい 'W' 'a' 'k' 'a' 'y' 'a' 'm' 'a' '\0' 20 型修飾子 const: その識別子の値はプログラムによって変更できな い.参照は可能. volatile: 処理系が知らないうちに値を変える可能性が ある.参照は可能. constの使用例: char *strcpy(char *dest, const char *src); *dest は 関数内実行中 左辺値にできる *src は 関数内実行中 左辺値にできない dest, srcともに 関数内実行中 左辺値にできる 21 並べ替え問題 処理対象:intの1次元配列 処理前: 4 7 3 8 6 1 2 5 処理後: 1 2 3 4 5 6 7 8 並べ替えは「ソート(sort)」,「ソーティング(sorting)」と も呼ばれる. 22 並べ替え問題:定義する関数 int get_array_length(int *array); void print_array(const char *message, int *array); 配列の要素数を求める. × sizeof(array)/sizeof(array[0]) メッセージと,配列の各値を出力する. void sort_by_selection(int *array); 選択法を用いて並べ替える. 配列の中身を書き換える. 23 関数の呼び出し関係 main print_array sort_by_selection get_array_length 24 まとめ 関数を自分で定義することができる.関数を呼び出すとき, 引数は値渡しで授受される. 変数には,型とは別の属性として,記憶域クラスと型修飾子 を指定できる.static変数とauto変数とでは,大きく挙動 が異なる. 「変数の定義」と「オブジェクトの生成」は別. オブジェクトの生成・破棄のタイミングに注意する. 25
© Copyright 2025 ExpyDoc