計算機実習 I
第九回 (2005年 6月 16日)
ポインタの応用
http://www.sw.it.aoyama.ac.jp/2005/Computer%20Practice%20I/lecture9.html
© 2005 Martin
J. Dürst 青山学院大学
プログラミングへの心構え
- sleep well! (but not during the class!!!)
- 関数を作るときに一般的な使い方を考える
ポインタの用途
- 低レベルのアドレス操作 (デバイス割り当てなど)
- 動的メモリの管理
- 配列の処理の効率化
- 参照 (関数への渡しなど)
- 間接 (indirection)
メモリの使い方
メモリは変数などの種類によってに分けられ、使用領域が違う。典型例:
FFFFFFFF |
|
|
スタック:
ローカル変数、引数、関数呼び出しに必要なもの |
|
↓↓↓↓↓↓↓↓ 下方向に伸び、使用後縮む
↑↑↑↑↑↑↑↑ |
|
空き領域
|
|
↑ ↑ ↑ ↑ ↑ ↑ およそ上方向に伸びる ↑ ↑ ↑
↑ ↑ ↑ |
|
ヒープ: プログラム中任意に必要なメモリ |
|
初期値を持たないグローバル変数 |
|
初期値を持つグローバル変数 |
|
関数などプログラムそのもの |
00000000 |
|
ヒープの使い方
- プログラムの途中にメモリを使いたいが、どのぐらい必要か予想できない
- 主な用途:
- 要素数が予想できない配列 (文字列を含めて)
- 要素の数が予想できないデータ構造
(木、リスト等)
- 使い方:
- メモリが必要なとき、メモリを用意する関数
(malloc 等) を呼ぶ
- メモリが要らなくなったとき、領域を解放する関数
(free) を呼ぶ
動的メモリの定石
定石の説明
if (!( /* メモリが足りるかのテスト */
pX = /* * 新しいメモリへのポインタの代入 */
(struct x*) /* 適切なポインタ型への変換 */
malloc(sizeof(struct x))) /* メモリの大きさの計算と関数の呼び出し */
{
printf("Not enough memory!\n"); /* エラー処理 */
exit(1);
}
動的メモリの関数
- malloc:
メモリブロックの確保、初期化なし、メモリが足りないときに
(void*)0 を返す
- calloc: メモリブロックの確保、 0000 で初期化
- realloc: メモリブロックの再確保
新場所 = realloc(旧場所、新サイズ);
pNew = realloc(pOld, 10);
- free: メモリブロックの解放
動的メモリの注意
- realloc, free
にはブロックの先端のアドレスしか使えない
- 解放後、再確保後の使用は禁物
- 確保と解放の責任を決める必要がある
- 要らなくなった領域を解放し忘れない (memory leak)
void *
と NULL
- ポインタの型が関係ないときには
void *
を使う
- 普通のポインタ型から又は普通のポインタ型への変換が可能
- ポインタがどこにも指してないときは
NULL
(0) を使う
- NULL
のポインタを参照するのはエラーになるので必要に応じて使う前にチェック
ポインタで配列処理の効率化
- 配列記述を処理するには (掛け算一つと)
足し算一つが必要
- 動くポインタを使えばその演算は要らない
- 配列の処理の終了も指数ではなくて、配列の終わりのアドレス
(配列の後に次の要素が来れる場所) を使える
- 同じ型のポインタは比較可能
ポインタと配列の組み合わせ
- 配列の配列: a[10][10]; a[][5]
- ポインタの配列: *p[20], *p[];
ポインタと配列の違い
(int a[20], *p;
を想定する)
- 確保されたメモリが違う
sizeof
の結果が違う
- 配列のアドレスは配列と同じ (
a == &a
)
- 関数に渡すと区別が分からなくなる
ポインタと構造体
- 構造体へのポインタは非常に多い
- ポインタからの構造体メンバの参照には
->
演算子が使える
- 方程式:
p->m
≡(*p).m
構造体をポインタでつなぐ
構造体をポインタでつなぐことがよくある
(リスト、ツリー等)
構造体のメンバの一つを構造体へのポインタにする
その場合には無名の構造体が使えない。
例:
typedef struct line {
char *s;
struct line *next;
} line;
ポインタと関数
- 関数へのポインタも取れる (関数ポインタと呼ぶ)
- こういうポインタを関数に渡すことが出来る
- 定義:
int (*kanp)(int a, int b);
- 実際の関数のアドレスを取るときには
&
はあってもなくても良い:
kanp = max; /* & 無し */
kanp = &min; /* & 有り */
- 呼び出す時には必ず
*
が必要:
result = (*kanp)(2, 3);
- 複雑な定義の場合は使い方を見よう!
演習について
- 09A1: 06C2
を出来てない人は先週配ったプログラムを使う。用意された入力ファイルを使う。
- 09A2: 関数へのポインタも出力
- 09B2: alignement (アラインメント、メモリの最適配置)
- 09B3: 今日はあるメモリ領域 (void * と長さ)
をバイトごと二進数で出力するプログラムだけを作る。来週追加説明がある。
- 09C1、09C2
は来週に回してもよい。逆に、今まで残された問題があれば速めにやる。
課題について
次回の準備
- C: ドライブに作ったプログラムを忘れずに Z:
ドライブに移す
- 今日の復習。来週もミニテストがある。
- 新訂新 C 言語入門の第 8 章 (ポインタ、pp. 203-228)
を必要に応じて読み直す。