malloc() 関数による、2次(的)配列

 
256 バイトの文字列を、100 個扱いたい!
( 解説なし/ 多少おせっかい/ くどい & 見づらい 各版 )

 

256 * 100 を最初に確保せず、「まず」は、箱 ( 4 :sizeof(char *) * 100 個 : 400バイト) を用意することから始まるので、直感的にはわかりにくい 

解説なし(ストレート)

char **pp ;
pp = (char **)malloc( 100 * sizeof( char * )) ;
for( char **p = pp ; p - pp < 100 ; p ++ ) { *p = (char *)malloc( 256 ) ; }
// 開放
for( char **p = pp ; p - pp < 100 ; p ++ ) { free( *p ) ; }
free( pp ) ;

 malloc( 256 ) は、対照的に書くなら malloc( 256 * sizeof( char ))
 掛け算の順序、256 * sizeof( char *)sizeof( char *) * 256 の違いについてはつっ込まないこと。

 

多少おせっかい版 (やっていることは同じ)

char **pp ;
pp = (char **)malloc( 100 * sizeof( char * )) ; // 4 バイトの箱 100 個(400Bytes)
for( char **p = pp ; p - pp < 100 ; p ++ ) // 256文字(バイト)分を100 個 確保
{ *p = (char *)malloc(
256 ) ; } // このループが 100 回。合計 256 * 100 バイト

for( char **p = pp ; p - pp < 100 ; p ++ ) // 開放のループ 100回
{ // memset( *p , 0 , 256 ) ; // やってもいい
free( *p ) ; // 開放
// *p = NULL ; // やってもいい
}
// memset( pp , 0 , 100 * sizeof( char *)) ; // やってもいい
free( pp ) ;// 400バイト分がいっきに開放される
//
pp = NULL ; // やってもいい
//
memset( &pp , 0 , sizeof( char **)) ; // やってもいい (直上の行とまったく同じこと)

256 * 100 バイト確保したい目的とはうらはらに、4 バイトの箱 100 個分 : 400Bytes が 余分に消費されたことに注意。

 

くどい & 見づらい版 (やっていることは同じ)
こういうとき table を使うと見易さが違いますね・・・


char **pp ; // 「ポインタのポインタ」 を用意
pp = (char **)malloc( 100 * sizeof( char *) ) ;
// ポインタ用の箱を 100 個用意。確保された先が、pp 。
// pp 〜 pp + 99 の範囲内で、*( pp + 1 or 2 or 3 or ... 99) のように、
// 、参照、アクセスすることが可能。・・・代入ももちろん可能。
// 仮に、256 文字を 100 個用意したいとして、
// はじめから安直に 256 * 100 バイトを確保しない。
for( char **p = pp ; p - pp < 100 ; p ++ ) // 変数を用意せず、p - pp によって、100 と比較
{
*p = (char *)malloc( 256 ) ;
// 256 バイト確保(malloc)して、毎回そのポインタを "一個一個" 、
// さっきかくほされた箱にいれていく(代入、= )。全100回
// これを 100 回確保するから、計 256 * 100 バイト 確保されることになる。
// ここでやることだから、前もって 256 * 100 バイト確保する必要はなかった。
}
// 結局、消費したメモリーは、この時点で、
// ポインタ分の 100 * 4 ( == sizeof(char *) ) バイト、及び、
// 具体的文字分 256 * 100 バイトという計算となる。

// 開放。理解していないと開放もできない。
// まずは、文字分から開放する。ゆめゆめ、free( pp ) ; からやってはいけない。
// なぜなら、pp を開放してしまっては、
// 具体的文字分 ( 256 * 100 バイト分) を開放するすべがない。
for( char **p = pp ; p - pp < 100 ; p ++ ) // p をインクリメントしていき、その都度、 *p の中身を参照して、そういうふうにやっていく。
{
// 開放するまえにきれいにしてもいい。
memset( *p , 0 , 256 ) ;

// 256 バイト分を、開放する。
free( *p ) ;

// なんなら、開放したあと、ポインタの箱を、きれいにしてもいい。
*p = NULL ;
}
// なんなら、最後に 100 個の箱をぜんぶきれいにしてもいい。
memset( pp , 0 , 100 * sizeof( char *)) ; // memset の第3引数は "バイト数"。

// 最後に
free( pp ) ;

// いままでみたいにきれいにするなら、
pp = NULL ;
memset( &pp , 0 , sizeof( char **)) ; // こちらは混乱するだけか?





以上とは別のやりかた(別のアルゴリズム)。
256 * 100 を最初に確保してしまうやりかた。
( 256 バイトの文字列を、100 個扱いたい!点はおなじ)


char *_p = (char *)malloc( 256 * 100 ) ; // あとは開放すればよい。

// このままで、accessiblity を得るには、int i; を用意した上で、
// p + 256 * i のようにアクセスする。ただし、0 <= i < 100
// p + 256 * i でアクセスする限り、
// ( p + 256 * i を、いつもプログラムに記述してこの確保されたメモリを利用していくつもりなら、)
// 以下の斜体部分必要ない
// また、その場合は、今までの例とは異なり、余分なメモリは消費しない。
// その場合は、ジャスト 256 * 100 バイトの消費で済む。

// 前の例に
近づけるには・・・・・
char **pp = (char **)malloc( 100 * sizeof( char *) ) ;
// 結局は、100 * 4 ( == sizeof(char *) ) バイト、
さきの例と同じ 400 バイト用意することになる
for( char **p = pp ; p - pp < 100 ; p ++ )
{ *p = _p + 256 * ( p - pp ) ; }
// 箱に入れていく。
// 開放
memset( pp , 0, 100 * sizeof(char *) ) ; // やってもいい
for( char **p = pp ; p - pp < 100 ; p ++ ) { free( *p ) ; }
誤りである。
free( pp ) ;

// おおもとの開放
free( _p ) ;

 

案ずるよりうむがやすしとは、このページのための言葉です。
また、書いた当人もめんくらっております。

 

 

[戻る]