C プログラミング入門 総機 1 ( 月 1) 11: 動的メモリ確保 Linux にログインし 以下の講義ページを開いておくこと http://www-it.sci.waseda.ac.jp/ teachers/w483692/cpr1/ 2015-06-22 1
まとめ : ポインタを使った処理 内容 説明 呼び出し元の変数を書き換える第 9 回 文字列を渡す 配列を渡す 第 10 回 ファイルポインタ 第 10 回 複数の値を返す 今回 大きな領域を確保する 今回 2015-06-22 C プログラミング入門総機 1 ( 月 1) 2
複数の値を返す return は 1 つの値しか返せない 複数返すには 書きこんでもらいたい変数へのポインタを渡す 返り値を使って計算 整数の配列の先頭ポインタとその個数 例 :int 配列の最大 最小値を計算する int maximum(const int *a, int n); int minimum(const int *a, int n); void minmax(const int *a, int n, int *m, int *M); 最小値を書き込むアドレス最大値を書き込むアドレス返り値を使わない ( 変数などへのポインタ ) ( 変数などへのポインタ ) ( 使ってもいい ) 書き換えるので const がつかない 3 2015-06-22 C プログラミング入門総機 1 ( 月 1)
整数部と小数部を求める関数 double modf(double value, double *iptr); 戻り値 :value の小数部 ( 符号付き ) iptr の指すメモリ領域 : 整数部 ( 符号付き ) #include <stdio.h> #include <math.h> int main(void) { double ipart, fpart; fpart = modf(32.5, &ipart); 小数部 0.5 は戻り値として返る 標準ライブラリ関数の例 double ipart 2015-06-22 C プログラミング入門総機 1 ( 月 1) 4 32 変数 ipart のアドレスを渡すことで 関数 modf は ipart の中身を書き換えることができる
ポインタ変数の初期値の注意 通常の変数同様 ポインタ変数も初期化されない しかし どこかは指している 初期化しないポインタ変数でアクセスすると危険 { int *p; *p = 100; 暗黙の初期化はされないので どこを指すアドレスが入っているかは不明 p 2015-06-22 C プログラミング入門総機 1 ( 月 1) 5
NULL ポインタによる安全策 以下の場合 NULL ポインタを入れるとよい 初期化時にアドレスを決定しないもの 今まで使っていたアドレスがもう使われない場合 { int *p = NULL; *p = 100; ポインタ変数の値 ( アドレ NULL ポインタが指す p ス ) が NULL ポインタの場領域にアクセスすると 合 斜線を引いて どこもシステムが検知して 指示していないことを表現 例外を発生する することがおおい 2015-06-22 C プログラミング入門総機 1 ( 月 1) 6
どのアドレスでもないことを示す特別な値 空ポインタともいう null ポインタ null ポインタに対してデリファレンス演算子 * を使うと例外 (null pointer exception) が発生する null ポインタ定数マクロ NULL を使って判定する 数値リテラル 0 は null ポインタに変換される <stddef.h> で定義されているが <stdio.h> などで自動的にインクルードされる 7 2015-06-22 C プログラミング入門総機 1 ( 月 1)
null ポインタ判定 以下のような書き方のバリエーションがある p が null であるか p が null でないか if(p == NULL) if(p!= NULL) if(p == 0) if(p!= 0) if(!p) if(p) if は常に式の評価値が0でないことを判定する ので 上段の式と同じ意味になる 2015-06-22 C プログラミング入門総機 1 ( 月 1) 8
配列を返す? 関数内で定義された配列変数は 通常の変数と同様に関数の実行が終了する際に消滅する... unsigned char *createnewimage(void) 配列の型を戻り値とする関数 { は作れない unsigned char img[512*512] = { 0 }; その代わり ポインタを返す return img; createnewimage の領域 } この領域は関数の実行後 img 消滅してしまう int main(void) { pimage unsigned char *pimage;... pimage = createnewimage(); // この後ポインタ pimage を使って返されたポインタ ( アドレス ) は す // アクセスしてはいけないでに意味のある情報を持っていない 9 2015-06-22 C プログラミング入門総機 1 ( 月 1)
自動的に消滅しないメモリ領域を作る 動的メモリ確保 (dynamic memory allocation) 変数のような言語機能ではなく 標準ライブラリ関数を使って 直接 OS にメモリ領域を要求する 確保された領域は自由に使える 変数名はつかないので ポインタでアクセスする 自動的には消去されないので 自分で解放する 2015-06-22 C プログラミング入門総機 1 ( 月 1) 10
malloc() 関数によって指定したバイト数のメモリ領域を確保する 確保された領域が置かれているところをヒープ (heap) という メモリ領域は初期化されない メモリ不足などで確保が失敗した場合 NULL ポインタが返る malloc() のプロトタイプ 動的メモリの確保 #include <stdlib.h> void *malloc(size_t size); 次のスライドで説明 2015-06-22 C プログラミング入門総機 1 ( 月 1) 11
確保されたメモリ領域の値 malloc は void * 型を返す { 特定の型を表さない 任意のポインタ型に自動変換できる int *pint; double *pdouble; ヒープ領域 C++ では自動変換されないため static_cast が必要 100??????????? 3.14 pint = malloc(16); 指し示すアドレスは同じだが 扱われ方が型によって異なる pint[0] = 100; // double の場合 // pdouble = malloc(16); // pdouble[0] = 3.14; int *pint double *pdouble sizeof(int) == 4, sizeof(double) == 8 の?? 場合 main のスタック領域 2015-06-22 C プログラミング入門総機 1 ( 月 1) 12
例 : int 型の 100 個の領域を確保 int *mem = malloc(sizeof(int) * 100); mem[0] ~ mem[99] としてアクセス 例 : 画素値を unsigned char 型として w h のサイズのメモリ領域を確保 unsigned char *image = malloc(sizeof(unsigned char) * w * h); 座標 x, y に対して image[x+w*h] としてアクセス 0 x < w, 0 y < h サイズの指定 sizeof(unsigned char) は必ず 1 なので 書かなくてもよい 2015-06-22 C プログラミング入門総機 1 ( 月 1) 13
動的メモリの確保 ( その他 ) calloc(): malloc() と同じだが さらにゼロで初期化を行う size num バイトの領域を確保する メモリの全てのビットが 0 となるからと言って double などの型の値として 0 となるとは限らない realloc(): 確保された領域のサイズを変更 calloc(), realloc() のプロトタイプ #include <stdlib.h> void *calloc(size_t num, size_t size); void *realloc(void *ptr, size_t new_size); 2015-06-22 C プログラミング入門総機 1 ( 月 1) 14
確保したメモリへのポインタを指定して その領域を解放する 解放した領域は使用してはならない NULL を入れておくと安全 既に解放済みのポインタに対して実行してはいけない (2 重解放エラー ) NULL ポインタを与えた場合は何もしない free() のプロトタイプ 動的メモリの解放 #include <stdlib.h> void free(void* ptr); どんな型のポインタも void * に自動的に変換される 2015-06-22 C プログラミング入門総機 1 ( 月 1) 15
例題 :PGM 画像 画像のためのメモリを自動的に確保する関数... // 画素値 0 で初期化された画像を動的に作成する unsigned char *createimage(int width, int height) { int i; unsigned char *img = malloc(width * height); if(img == NULL) sizeof(unsigned char) == 1 な { ので 掛けるのを省略 return NULL; } メモリ不足の場合は NULL を返す for(i = 0; i < width*height; ++i) img[i] = 0; return img; } すべてゼロで初期化する 2015-06-22 C プログラミング入門総機 1 ( 月 1) 16
例題 :PGM 画像 使い終わったら自分で free() する... int main(void) { unsigned char *Image = NULL;... Image = createimage(640, 480);... free(image), Image = NULL; メモリ領域を解放する ポインタは無効になるので NULL ポインタを代入しておくとよい 2015-06-22 C プログラミング入門総機 1 ( 月 1) 17
用語 : メモリリーク (memory leak) malloc() で動的確保した領域を free() で解放し忘れて メモリが圧迫されること 単純なプログラムではこの手のバグは見つけやすいが 秋期 C プログラミング で扱う複雑なデータ構造では発生しやすく 見つけづらいバグとなる 2015-06-22 C プログラミング入門総機 1 ( 月 1) 18
変数とポインタと動的メモリ確保の整理 メモリに領域を確保して値を読み書きする方法には 4 つある 分類生存期間スコープメモリ領域初期化 自動変数 ( ローカル変数 ) 大域変数 ( グローバル変数 ) 静的変数 (static 変数 ) 動的メモリ 定義位置からブロック終端まで プログラムの実行開始から終了まで プログラムの実行開始から終了まで 確保から解放まで 定義位置からブロック終端まで 定義位置からプログラム終了まで 定義位置からブロック終端まで スタック 静的領域 静的領域 ヒープ 初期化が指定されている場合のみ ブロックに入るたびに初期化される プログラム開始時に 1 度だけ 初期化が指定されない場合 0 で初期化される 変数名で参照しない 2015-06-22 C プログラミング入門総機 1 ( 月 1) 19 同上 malloc() はされない calloc() は 0 を書きこむ
配列変数と動的メモリ確保の比較 機能配列動的メモリ確保 サイズ 多次元 自動変数の場合は あまり大きなサイズは確保できない 可能 ただし 関数などに次元やサイズを伝えられない ほとんど制限がない 単なるポインタなので不可能 コピー不可能 標準ライブラリ関数を利用不可能 標準ライブラリ関数を利用 解放 アクセス 自動変数の場合 ブロックを抜けると自動的に消滅する 変数名が先頭へのポインタになる free() によって自分で解放する malloc() によって得られるアドレスをポインタ変数に入れて扱う 2015-06-22 C プログラミング入門総機 1 ( 月 1) 20