スライド6

プログラミング演習Ⅱ
第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へ直接質問しに来ても構いません