プログラミング演習Ⅱ 第6回 関数(2) 情報・知能工学系 山本一公 [email protected] 課題3の採点結果から • プログラムはほぼ全員OK • インデントが正しく入ってない – そもそもインデントが入っていない • 入れてください – インデントの深さが正しくない • ブロック毎にインデントを1段深くする – インデント幅が統一されてない • 1段につきスペース2個分とか、タブ1個分とか、自分で決め て、 それを守る 前回の課題の解説・ポイント(1) • 課題4-1 – List 6-2 を改造すれば良い – 教科書のような実装ではない例 int min3(int x, int y, int z) { if (x > y) { if (y > z) return z; else return y; } else { if (x > z) return z; else return x; } } 前回の課題の解説・ポイント(2) • 課題4-2 – シンプルな実装例 int prime(int x) { int i; if (x < 2) return 0; /* xが2より小さければ 素数ではないので即リターン */ for (i = 2; i < x; i++) { if (x % i == 0) return 0; /* xがxより小さい数で 割り切れれば素数ではない */ } return 1; /* ここまで来たらxは素数である */ } 今日の内容 • 教科書 pp.130~145 • 関数の続き – – – – 配列の関数への受け渡し 関数で配列に書き込むと…… const型修飾子 多次元配列の受け渡し • 変数の有効範囲と記憶域期間 – 有効範囲(スコープ) – 記憶域期間(autoとstatic) 配列の関数への渡し方(1) • まず、1次元配列 – 関数定義側では、”[]”だけを付ける int function(int x[], int no) /* int *x でも良いが、 厳密には意味が異なる*/ { return x[no]; } • 要素数を書いても間違いではない – 呼び出し側では”[]”を付けない int array[100], num, value; value = function(array, num); 配列の関数への渡し方(2) • 関数への値の渡され方は「値渡し」 – 先週の話 – 関数の仮引数と実引数は無関係 • 仮引数の値を関数内でどう弄っても、実引数の値 は変わらない • 仮引数へは実引数の値がコピーされるから – 配列の場合はどうか? • 配列の中身は……変わってしまう! • 例:p.132, List 6-12 配列の関数への渡し方(3) • 変わってしまう理由 – 関数に渡される配列は、配列全体がコピーされて渡 されるのではない – 配列名そのものが実は変数で、そこにはメモリ上の 配列の先頭アドレスが入っている – 関数にはそれがコピーして渡されている – だから、関数の中で見ている配列は、呼び出し元で 見ている配列の実体そのものになる! – ……ちょっと難しいね • メモリ上のアドレスを扱う変数型 – それが「ポインタ」!!(詳しくは10章で) 配列の関数への渡し方(4) • 配列の中身を書きかえられると困る – 書きかえられないようにしよう! – 「const型修飾子」を使う – “const”はconstant(定数)の意 int function(const int x[], int no) { x[no] = 0; } • こうやると書きかえられない(左辺値になれない) • 左辺値にすると、コンパイルエラー – 上のサンプルはコンパイルエラーになる 逐次探索・番兵法 • 逐次探索 – 順番に目的のものを探していく方法(アルゴリズ ム) • 番兵法 – 配列の末端に“番兵”と呼ばれるデータを追加しておく ことで、配列の末端を見つけやすくする手法 • アルゴリズムの話なので、省略します – 各自で読んでおいてください 配列の関数への渡し方(5) • 多次元配列の場合(例は2次元配列) void mat_add(const int ma[2][3], const int mb[2][3], int mc[2][3]) { … } int main(void) { … mat_add(ma, mb, mc); … } – 最初の要素数だけは省略可(初期化と同じ) void mat_add(const int ma[][3], const int mb[][3], int mc[][3]) { … } 有効範囲と記憶域期間(1) • 変数の有効範囲 – 関数の中で宣言した変数は、その関数の中だ けで有効 • main()とそれ以外の関数の中で同じ名前の変数を 宣言して使ってても全然大丈夫 – 関数の外でも(main()の外でも)変数は宣言 できる • 有効範囲はどこ? • 「有効範囲」と書いて「スコープ」と読みます 有効範囲と記憶域期間(2) • 分かりやすい例:p.141, Fig.6-12 – 関数の外側で宣言されている”x”は全体で有効 • 厳密にはこのソースファイル内で有効 • 「ファイル有効範囲」(p.126) • 関数 print_x() 内では”x”という名前の変数は宣言されていないので、この関 数の中の”x”は、関数の外側で宣言されている”x”のこと – main() の中で宣言されている”x”は、ピンクの範囲で有効 • main()のブロック”{””}”の中で有効 – forループの中で宣言されている”x”は、ブルーの範囲で有効 • for文は”{””}”でブロックを作る。ブロックの先頭なら変数を宣言で きる – 同じ名前の変数が違う有効範囲で宣言されている場合、一番内 側の有効範囲の変数が有効で、それ以外の有効範囲の変数は見 えなくなります(「隠蔽される」) 有効範囲と記憶域期間(3) • 記憶域期間:静的変数と動的変数 – 静的変数(静的記憶域期間) • プログラム開始時(main()の実行前)に生成され、プログラ ムの終了時に消滅する • 関数の外側で宣言するか、“static”を付けて宣言すると静的変 数になる – 動的変数(自動記憶域期間) • プログラムの流れが宣言を通過する際に生成され、ブロック の終点(”}”)を通過する際に消滅する • 関数内でstaticを付けずに宣言すると動的変数になる – ”auto”識別子は事実上使い道がない – “register”識別子は最近のコンパイラではほぼ効かない 有効範囲と記憶域期間(4) • p.143, List 6-19のプログラムとpp.144~145の説明 – fxとsxは静的変数、axは動的変数 – fxとsxはプログラムの開始から終了まで存在している • 関数が前に呼び出された時の値を保っているので、関数が呼 び出される度に値が増えていく • 初期化は変数の生成時に行われる。sxの初期化が関数が呼び 出される度に行われるわけではないことに注意。 – axは関数が呼び出されると生成され、関数が終了する と消滅する • 関数が呼び出される度に初期化が行われるので、毎回0に なっている 今週の課題 1. 教科書 p.133, 演習6-9のプログラムを作成せよ。演習6-1 の問題文にあるように、main関数なども作成して完成 したプログラムを作ること。 2. 正の整数値 X, Y, Z, W に対して、数列 ìï W an = í Z (X × a +Y ) mod 2 ïî n-1 n =1 n = 2, 3,… {ak k =1,2,… } は を考える。X, Y, Z, W をうまく与えると数列 乱数のような数値列を与えるという(線形合同法)。 X=109, Y=1021, Z=15, W=13として、呼び出す度に乱数 を1つ返す関数 int lcg_rand(void) を作成せよ。そして、 この関数を用いてmain関数で100個の乱数を発生、表示 させよ。 レポートについて • 電子メールで提出 – 提出先は [email protected] – Subjectを「プログラミング演習2 課題5提出 号・氏名 」とすること – C言語ソースファイルを添付する 学籍番 • メールの本文には何も書かなくて良いです – ソースファイルの頭にコメントで以下の情報を入れる • 学籍番号・氏名 • プログラムの説明(どのように動くのか、工夫した点等) • 実行結果(長い場合は一部)を貼る – 提出締切は、11月14日(水) 12:00 (1週間後) 授業用Webサイト • URL: http://www.slp.cs.tut.ac.jp/~kyama/programming2/ – 課題のpdfファイルが置いてあります。 – 授業で使ったpptファイルを置いていきます。 • 質問メールは、以下のどちらかのアドレスまで – [email protected] – [email protected] • C-515へ直接質問しに来ても構いません
© Copyright 2024 ExpyDoc