前回の授業で説明していたような、必要に応じて確保するメモリは、動的メモリと呼ばれる。
動的メモリも、局所変数やalloca()を用いたスタック領域と、malloc()とfree()を使うヒープメモリ領域に分類される。
strdup
前回の文字列の確保の説明では、malloc()とstrcpy()をあわせて実行していたが、C言語ではこういった処理が多いので、専用の関数 strdup() がある。
char str[] = "abcdefg" ; char*pc ; if ( (pc = (char*)malloc( strlen( str ) + 1 )) != NULL ) { strcpy( pc , str ) ; } // おなじことを strdup では... pc = strdup( str ) ;
様々なメモリ確保
自分で定義した構造体を、malloc で領域確保しながら使う場合、1次元配列や2次元配列を作る場合、色々な確保の方法がある。
// 複素数を例に struct Complex { double re ; double im ; } ; // 基本 struct Complex a ; a.re = 1.0 ; a.im = 2.0 ; // ポインタで確保 struct Complex* b ; b = (struct Complex*)malloc( sizeof( struct Complex ) ) ; if ( b != NULL ) { b->re = 1.0 ; b->im = 2.0 ; } // 一次元配列 struct Complex c[ 2 ] ; // 通常の使い方 c[0].re = 2.0 ; c[0].im = 3.0 ; c[1].re = 4.0 ; c[1].im = 5.0 ; // 一次元配列を動的に確保 struct Complex* d ; // Complexの配列 d = (struct Complex*)malloc( sizeof( struct Complex ) * 2 ) ; if ( d != NULL ) { d[0].re = 2.0 ; d[0].im = 3.0 ; d[1].re = 4.0 ; d[1].im = 5.0 ; } // 一次元のポインタ配列 struct Complex* e[ 2 ] ; // Complexのポインタの配列 e[0] = (struct Complex*)malloc( sizeof( struct Complex ) ) ; if ( e[0] != NULL ) { e[0]->re = 2.0 ; e[0]->im = 3.0 ; } e[1] = (struct Complex*)malloc( sizeof( struct Complex ) ) ; if ( e[1] != NULL ) { e[1]->re = 4.0 ; e[1]->im = 5.0 ; }
C++での new, delete 演算子
複雑なデータ構造のプログラムを作成する場合には、このような malloc() , free() をよく使うが煩雑であるため、C++ではこれらをすっきりと記述するために、new 演算子、delete 演算子があり、それぞれ malloc(), free() に相当する。
// 単独 Complex* b = new Complex ; b->re = 1.0 ; b->im = 2.0 ; delete b ; // 配列 Complex* d = new Complex[2] ; d[0].re = 2.0 ; d[0].im = 3.0 ; d[1].re = 4.0 ; d[1].im = 5.0 ; delete[] d ; // 配列のdeleteには[]が必要 // ポインタの配列 Complex* e[2] ; e[0] = new Complex ; e[0]->re = 2.0 ; e[0]->im = 3.0 ; e[1] = new Complex ; e[1]->re = 4.0 ; e[1]->im = 5.0 ; delete e[0] ; delete e[1] ;
2次元配列
2次元配列の扱いでも、注意が必要。
int cs = 何らかの値 ; // データ列数 int rs = 何らかの値 ; // データ行数 int a[ rs ][ cs ] ; // C言語ではエラー a[ y ][ x ] = 123 ; // 1次元配列を2次元配列のように使う int* b ; b = (int*)malloc( sizeof( int ) * rs * cs ) ; b[ y * cs + x ] = 123 ; // b[ y ][ x ] への代入 // 配列へのポインタの配列 int** c ; c = (int**)malloc( sizeof( int* ) * rs ) ; // NULLチェック省略 c[0] = (int*)malloc( sizeof( int ) * cs ) ; c[1] = (int*)malloc( sizeof( int ) * cs ) ; : c[ y ][ x ] = 123 ;
レポート課題
メモリの動的確保の理解のために、自分の理解度に応じて以下のプログラムのいずれかを作成せよ。
ただし、プログラム作成時には、配列サイズは未定で、プログラム起動時に配列サイズを入力するものとする。
- 固定長の名前で、人数が不明。
- 長い名前かもしれない名前で、人数も不明
- 複素数のデータで、データ件数が不明。
- 名前と電話番号のデータで、データ件数が不明。
このような状況で、データを入力し、検索などの処理を通して自分のプログラムが正しく動くことを検証せよ。
レポートには、プログラムリスト、プログラムの説明、動作確認した結果、考察を記載すること。
C++のvectorクラスを使ったら
// C++であればvectorクラスを使えば配列なんて簡単 #include <vector> int main() { // 1次元配列 std::vector<int> a( 10 ) ; for( int i = 0 ; i < 10 ; i++ ) a[ i ] = i ; // 2次元配列 std::vector< std::vector<int> > b( 9 , std::vector<int>(9) ) ; // ↑ ここの空白は重要 for( int i = 0 ; i < 9 ; i++ ) { // ">>" と書くとシフト演算子 for( int j = 0 ; j < 9 ; j++ ) { // "> >" と書くと2つの">" b[i][j] = (i+1) * (j+1) ; } } return 0 ; }