3週目:ポインタの配列と動的確保[全体PDF]

今週の目標

ユーザが指定した画像ファイルを読み込み,そのコピーを別ファイルとして出力する(画素数が異なるファイルにも対応させる)


図1 3週目の目標

画素値の格納方法の改良 ①動的な一次元配列[PDF]

mallocとfree


図2 メモリの静的確保と動的確保の比較例

図2に,要素数3のunsigned charの配列を確保して値を代入するコードの例を示す. 動的配列の場合,確保した領域の先頭のアドレスを保持する必要があるため, 変数はポインタ型となる. malloc(またはcalloc)関数により,連続したメモリ領域を確保し,その先頭アドレスがaに代入される. メモリ領域を確保した後であれば,aを利用することで静的配列と同様に各要素にアクセスできる. そしてメモリ領域を保持する必要がなくなった時点で,free関数により領域を解放する.

図2の例では動的確保を行う理由は特にないが,プログラムを実行するまで配列の要素数(例では3)が確定しない場合, 静的配列を確保することは一般的には不適切である(ただし,C99以降は確保できる場合もある). 動的確保であれば,mallocの引数中の3を変数に置き換えれば対応できる. 本演習課題では,ユーザが画像を指定するまで画素数がわからないため, 画素値を格納するpBufferは静的ではなく動的に確保すべきである.

作業課題

PPMのヘッダ情報に合わせてpBufferのサイズを動的に確保できるように改良せよ. ただし,pBufferは一次元配列でよいものとする(二次元配列は次の演習課題で作成する). さらに,img01.ppmとはサイズが異なる以下の画像も読み込めることを確認すること.

画素値の格納方法の改良 ②二次元配列[PDF]

ポインタの配列


図3 一次元配列による画素値の管理

図3に,一次元配列を動的に確保して先頭アドレスをpBufferに格納した状態を示す. ここで,オレンジはアドレスを格納している場所(つまりポインタ), 青は画素値を格納している場所を表している. また,説明を簡単にするため,全て十進数で表現し,ポインタの領域サイズは1バイトを想定している. これまでの作業により画素値は動的な一次元配列に格納されているため, 異なるサイズの画像ファイルが指定されても画素値は柔軟に格納できる. しかし,図3のデータ構造では, ある特定の画素(例えば,上から3行目,左から4列目)にアクセスしたい場合, 一次元配列上でのインデクスが何番目になるかは計算して求める必要がある. よって,一次元配列は画素値の管理としては直観的に扱いにくいことがわかる.


図4 二次元配列による画素値の管理

次に,図4のように,画像の各行の画素値を別々のメモリ領域で管理することを考える. 一行分,つまりsizeof(PIXEL) * xsize分の連続した領域(アドレス40から開始される領域)をmallocで確保し, その先頭アドレスである40をPIXEL*型の変数で管理する. これをysize分繰り返すと,PIXEL*の変数はysize個必要になる. つまり,アドレス24から始まる領域は,ポインタの配列として管理すると楽である. 各行の画素値領域の先頭アドレスをポインタ配列で管理することで, 二次元的なアクセスを容易に行えるようになる.

ここで,PIXEL*型の配列(図中のアドレス24から開始される配列)は, 画像を読み込むまでサイズが確定しないため, これをさらに動的な配列として確保する必要がある. PIXEL*を個々の要素とする配列を動的に確保したい場合, その先頭アドレス(24)を管理するための変数の型はPIXEL**となる.

演習課題4_3_1

動的確保によるポインタ配列を利用し,pBufferを2次元配列として確保できるようにプログラムを改良せよ. さらに,img01.ppmとimg02.ppmのどちらの画像もコピーできることを確認すること.