プログラミング 4 記憶の割り付け
変数の特徴 名前がついている(名前を呼んで使う) 使う分はあらかじめソースコードに書いておく 管理はプログラム側でしてくれる 配列の要素数はコンパイル時に決まっていないといけない 管理はプログラム側でしてくれる 扱いは簡単
記憶の割り付け 名前がついていない(ポインタで指す) 使う分をその都度プログラム中で要求する 管理はプログラマが行う 欲しい分だけ記憶領域を確保できる 管理はプログラマが行う 注意して扱わないといけない
malloc() 関数 引数で指定したバイト数の記憶領域を確保して,その 領域の先頭アドレスを void * 型で返す malloc(4) ← 4 バイト確保する この方法で確保した記憶領域は「名前がついていな い」ので,ポインタで指して使わなければならない この「ポインタが指している状態」をプログラマが管理しな ければならない 指せなくなると「メモリリーク」という重大な不具合につな がる #include <stdlib.h> で使えるようになる
sizeof 演算子 かっこ内の型が何バイトを要するかを調べる演算子 sizeof(int) ← int 型が何バイトか調べる malloc() 関数で確保するバイト数を計算するのによく用いる malloc(sizeof(int)) ← int 型と同じバイト数を記憶を確保
void * 型 「ポインタの型」は「指す先に何があるか」を表して いる int * 型:指す先に int がある double * 型:指す先に double がある void * 型は「指す先に何があるかの情報がない」 使うときには通常「指す先を何の型で解釈するのか」の情報 を与えるために型変換(キャスト)をする int と同じサイズの記憶領域を確保して,int として使えるよ うにする (int *)malloc(sizeof(int))
malloc() 関数の使い方 確保する記憶領域を指すポインタを用意する int *p; malloc() 関数で記憶を割り付ける;型変換に注意 p = (int *)malloc(sizeof(int)); これが常套句! 割り付けた記憶領域をポインタを経由して使う その記憶領域を指すポインタがなくならないように注意! 使い終わったら free() 関数で解放する free(p); 「使い終わったら後始末する」という手動での管理が必要
malloc() 関数は失敗する malloc() 関数は失敗することがあり,そのときは NULL を返す;通常は NULL チェックをする p = (int *)malloc(sizeof(int)); if (p == NULL) { …失敗時の処理…… } 簡単なプログラムでは,記憶の割り付けに失敗したら(その 先実行できそうになくなるから)プログラムを終了させてし まうことが多い if ((p = (int *)malloc(sizeof(int))) == NULL) { … } と一 気に書くこともある
記憶の割り付けの使い方(1) たとえばこんなプログラム int *p; p = (int *)malloc(sizeof(int)); if (p == NULL) { printf(“失敗しました\n”); return 0; } *p = 5; /* p の先に int の記憶領域がある */ printf(“%d\n”, *p); free(p); return 0;
構造体で使うことも多い たとえばこんなプログラム struct cplx { double re, im; }; …… struct cplx *c; c = (struct cplx *)malloc(sizeof(struct cplx)); if (c == NULL) { …… } …… free(c); あとで学ぶ「線形リスト」「二分木」では頻繁に使用 する
メモリリーク 確保した記憶領域を指すポインタがなくなってしまい, 操作不能(回収不能)に陥ること 記憶の割り付けは「プログラマが管理する」! 回収できないのでプログラム終了まで記憶領域を無駄に使用 し続けることになる→たび重なるとシステムが不安定になっ たりする 記憶の割り付けは「プログラマが管理する」!
割り付けで「配列」を作る(1) C 言語では配列の大きさはコンパイル時に決まってい なければいけない 配列の大きさをあとから決めることはできない 記憶の割り付けを使えば,あとから大きさを決められ る「配列と同等のもの」が作れる 配列は「記憶装置の連続した領域に確保される」ことから, 「記憶装置に連続した領域を確保すれば配列と同等に使え る」
割り付けで「配列」を作る(2) 型と個数を指定して割り付け int *p; p = (int *)malloc(sizeof(int) * 10); これで int×10 の記憶領域が確保され,大きさ 10 の配列と 同等に使える つまり *p,*(p + 1),*(p + 2),……,*(p + 9) が使え ることになるが,読み替えにより p[0],p[1],p[2],……, p[9] になる これはホンモノの配列ではないので,最後に free() 関数で解 放する必要がある
文字列ぴったりの記憶領域 文字列ぴったりの記憶領域を確保するのにも記憶の割 り付けが重用される ‘\0’ が入る分文字数+1 個を確保することに注意 たとえばこんなプログラム char s[256], *p; …s[] に何か文字列をセット … p = (char *)malloc( sizeof(char) * (strlen(s) + 1)); strcpy(p, s); ……