計算機実習 I
第十一回 (2014 年 6 月 19 日)
ポインタの応用: 参照と間接
http://www.sw.it.aoyama.ac.jp/2014/CP1/lecture11.html
Martin J. Dürst
© 2005-14 Martin
J. Dürst 青山学院大学
目次
- ミニテスト
- 前回のまとめ、演習問題
- 参照渡し
- 間接
- 引数としての関数
ミニテスト
- 授業開始までにログイン済み
- 授業開始まで教科書、資料、筆箱、財布などを鞄に入れ、鞄を椅子の下に
- テスト終了後その場で待つ
前回のまとめ
- 変数の種類によってスタック、ヒープ等に配置
- 配列の指数計算はポインタ演算 (足し算など)
に変換可能
- 動的メモリは
malloc
で確保、realloc
で大きさを調整、free
で返却
- 動的メモリでは「定石」の使用、返す責任者の決定が大切
前回の演習
- 未提出 (またはエラー): 10A1: 2人、10B2: 5人、10C1:
14人、10C2: 24人
- 10C3 は 9人が完成
- 10C3 の注意点:
- 読み込み済み文字の数と用意されたメモリの長さ
- 行頭の
EOF
: NULL
を返却
- 行中の改行:
改行の保存、読み込みを終了、
\0
を追加
- 行中の
EOF
: 読み込みを終了、\0
を追加
ポインタの用途
- 低レベルのアドレス操作 (デバイス割り当てなど)
- 配列の処理とその効率化
- 動的メモリの管理
- 参照 (関数への参照渡しなど)
- 間接 (indirection)
自然言語での参照
- 彼、彼女、彼ら
- これ、それ、あれ
- ここ、そこ、あそこ
引数の値渡しと参照渡しのイメージ
値渡し (call by value):
- 付箋に値を書いて、捨ててもいいように渡す
- 修理するものを直接渡す (例: 靴の修理)
参照渡し (call by reference):
- 箱の中に値を用意し、箱の場所を渡す
- 修理する場所 (住所) を指定 (例:
電線や水道管の修理)
引数の参照渡し
- 目的:
- 関数に値を渡すのではなく、
- 変数への参照を渡すことで
- 変数そのものが関数から変更可能
- 技法:
- 引数をポインタとして定義
- 関数呼び出し時にアドレス演算子 (
&
)
を使用
- 関数の中には常に間接演算子 (
*
)
を使用
- 問題点:
- 自動ではないので注意
(特に scanf
などコンパイラが型をチェックできない場合)
参照渡しの定石 (配列以外)
参照渡しの定石 (配列)
- 配列はポインタとほぼ同等で、参照渡しが自動
- 関数の中では配列とポインタが同等
(引数の定義では int *a;
と int a[];
は完全に同等)
- 配列の長さが分からないので注意が必要:
- 配列の終わりの印を決める (例: 11C1)
- 配列の長さを別の引数で渡す
配列の値渡しの方法
- 参照渡し、関数内で変更しない
- (1) で
const
で変更を不可能に
- 関数に渡す前にコピー
- 配列を構造体のメンバで定義、構造体を渡す
参照渡しの応用例: 複数の戻り値
課題: ある関数で n2 と n3
を同時に返す
解決策: 初期されてない参照渡し:
void square_cube(int n, int *pn2, int *pn3)
{
*pn2 = n * n;
*pn3 = *pn2 * n;
}
呼び出し側:
int a=5, a2, a3;
square_cube(a, &a2, &a3);
printf("a=%d, a*a=%d, a*a*a=%d\n", a, a2, a3);
間接 (indirection)
名言:
「全ての情報テクノロジーの問題は間接のレベルを一つ増やせば解決可能」
間接の例:
- IP アドレスの代わりにドメイン名
- 学生本人の代わりにあだ名、学生番号、メールアドレス
- データベースのインデックス
- Web ページ本体とそのアドレス (http://... など)
- Google, Yahoo みたいなサーチエンジン
C の場合には間接をポインタで実現
間接の応用例
順番を入れ替える時に、実際のデータを入れ代わると:
- データの移動が大変
- データの場所が変更、他のところで困る
- 同時に一つの順番しか使えない
間接を使えば全ての問題が解決 (例: 11C2)
関数に関数を渡す
関数 (のポインタ) も関数に引数として渡せる
典型例:
あるパターンで仕事して欲しいが、具体的な仕事が未決定
応用例:
相模原キャンパスの全ての講堂にある仕事をして欲しい
解決作:
- 関数「講堂たどり」を作る
- 「講堂たどり」に実際の仕事をやってくれる関数を渡す
- 「講堂たどり」が各講堂で実際の仕事の関数を呼ぶ
関数に関数を渡す応用例: 整列
- 整列の基準が色々ある
(学生番号、名前、成績、昇順・降順)
- 整列のやり方も色々、複雑
(バブル整列、挿入整列など、二年生後期のデータ構造とアルゴリズムで勉強)
- 一つの関数にまとめようとすると沢山の複雑な関数が必要
整列関数の種類
|
バブル整列 |
挿入整列 |
選択整列 |
クイックソート |
... |
学生番号 |
|
|
|
|
|
名前 |
|
|
|
|
|
数学の成績 |
|
|
|
|
|
英語の成績 |
|
|
|
|
|
... |
|
|
|
|
|
- お互いに独立 (直交)
- 二つの項目を比較する比較関数を整列関数に渡すと:
- 整列関数は一つで良い (ライブラリで用意可能)
- 比較関数が比較的簡単
関数に関数を渡す
C 以外のプログラミング言語
- C
プログラミング言語で関数の作成はある程度手間がかかる
- もっと簡単に関数が作れる言語がある: 関数型言語
(Lisp, Haskell など)
- 引数としてブロック (関数みたいなもの)
を渡すプログラミング言語: Ruby
@@@@in 2015, use 11C2_with_trace.c to show what happens
言語による差の具体例
Ruby: students.sort { |a, b| a.number <=> b.number }
C (11C2 から):
int compareNumbers (const void* a, const void* b)
{
Student *studentPa = *((Student **) a);
Student *studentPb = *((Student **) b);
return strcmp (studentPa->studentNumber,
studentPb->studentNumber);
}
...
qsort(byNumber, studentCount, sizeof(Student*), compareNumbers);
今日の演習
- 11A1: 参照渡しの基本
- 11B1: ポインタ (間接) を使う整列
- 11C1: 参照渡しを使って値を帰す
- 11C2: 学生データの整列 (@@@2015: tell students to not leave
early, better to complete part of 11C2 while in N604b;
関数を関数に渡す; 部分点;
一つの順番を追加するたびにプログラムチェッキングシステムに提出;
発展問題ではない; 締め切りは月曜日)
次回の準備
- 残りの演習問題を宿題として完成
- 今日の復習
- 参考書の第 11 章 (ポインタ、pp. 281-311)
をもう一回よく読む
- 参考書の第 12 章 (ファイル操作、pp. 313-345) を読む