『Cプログラミング診断室』目次次(第10章 最長不倒関数 いやらしい個所)

第10章 最長不倒関数

Free


この関数には、関数freeが何個あるでしょうか。関数全部を掲載していない ので、数えてもらえないのがとても残念です。中略した部分にもいっぱいfree が詰まっています。

grepで数えたら、573個ありました。約4行に1個の割合でfreeが出現して いるのです。こちらの方でも、最多出現率ということになるでしょうか。

freeとか、mallocなどの動的なメモリ割り当ての一連の関数は極めて危険な ものです。決して初心者が不用意に使用してはいけません。いい加減に使うと、 すぐプログラムは暴走します。こんな爆弾を、プログラムの中に多量にばらま いては、地雷がどこに埋まっているか分からないアフガニスタンとかカンボジ アの戦場跡を無謀にも散歩するようなものです。

とにかく、freeの使用は控えましょう。メモリの概念を理解できないまま、 こんな危険な関数を使うのは絶対禁止しましょう。

でも、この関数のなかでは、実際に可変長の巨大な領域を確保する必要があ るので、mallocやfreeを使用しない訳にはいきません。

■自由演技連続技

実際にfreeを呼んでいる箇所をみてみましょう。246行以降の処理に注目す ると、以下のようになっています。

    irtn = MakeSendMountDevice(
			    .........);

    if (irtn != NORMAL)
	{
	free(psend_mount);
	init_index_table(psend_index);
	free(psend_index);
	free(psend_color);

	return(ERROR);
	}
  

これは、このプログラマが頻繁にfreeを使用する典型的なパターンです。関 数を呼び出して、もしエラーだったら、それまでに確保した動的メモリ領域を 解放しています。上の例では、エラー発生時点で、それまでに確保されている 領域へのポインタ(psend_mount, psend_index, psend_color)をfreeの引数に 与えて解放しています。途中で、領域へのポインタを引数にしてインデックス・ テーブルの初期化(要するに元に戻す)もしているようです。

そして、領域の解放がすんだところで、ERRORを戻り値としてreturnしてい ます。これで、この関数はエラー終了したことが分かるし、関数内部で確保し た領域もきちんと掃除できたし、丁寧なエラーチェックをしているとでも思っ てコーディングしているのでしょうか。

■ワンパターン

246行以降がfreeの連続技でしたが、まったく同様のパターンが266行から始 まっています。とくに、freeの連続技の部分、つまり、

	free(psend_mount);
	init_index_table(psend_index);
	free(psend_index);
	free(psend_color);
の4行のパターンが、
	256 〜 259 行
	276 〜 279 行
	299 〜 302 行
	314 〜 317 行
	353 〜 356 行
と何度も出現します。さらに強力なfreeの連続技
    free(psend_mount);
    init_index_table(psend_index);
    free(psend_index);
    free(psend_color);
    free(pcolorno_table_record);
が384〜388行にあり、最後の方のエラーリターン 時の処理(2426〜2433行)は、
    unlink((char *)&sendfile_path_name[0]);
    free(psend_mount);
    init_index_table(psend_index);
    free(psend_index);
    free(psend_color);
    free(pcolorno_table_record);
    free(pdevice_control_record);
    free(line_length_data_record);
となっています。さらに、正常終了時にも次のよ うなfreeの連続技(2444〜2450行)があります。
    free(psend_mount);
    init_index_table(psend_index);
    free(psend_index);
    free(psend_color);
    free(pcolorno_table_record);
    free(pdevice_control_record);
    free(line_length_data_record);
最後の正常終了時のは別にしても、その他の自由演技(free連続技)は、簡単 にまとめることができるでしょう。

それにしても、よくこれだけ同じようなことが何度も現れるコーディングを するものです。使用しているエディタのコピー機能が優れているせいでしょう か。

■goto文

さて、この自由演技を、goto文を使ってまとめてみましょう。「goto文は絶 対禁止」という人もいますが、使い方によっては、大変重宝なものです。

正常終了(2457行)の、return(NORMAL)の後ろに、

  error:
    free(psend_mount);
    init_index_table(psend_index);
    free(psend_index);
    free(psend_color);
    free(pcolorno_table_record);
    return(ERROR);
を追加し、今まで「自由演技+エラーリターン」 になっていた箇所を、
	goto error; 
に変えます。もちろん、どこでエラーが発生しても同様に処理したいので、 error: の後ろにはもっと多くのfreeが詰め込まれます。でも、これだけでは うまくいきません。エラー発生時までに確保したメモリ領域は、エラー発生箇 所ごとに異なります。これをうまく処理するには、
  1. mallocなどから返された値を入れるポインタを全部NULLに初期化する。
  2. エラー発生時に、関数末尾のラベルerrorにgoto文で飛ぶ。
  3. error:の後に、freeを並べる。freeは、ポインタがNULLでないときのみ行なう。
の手順に従えばできるでしょう。したがって、最後のerror:以降は,例えば次 のようになるでしょう。
  error:
    if( psend_mount )
	free(psend_mount);
    if( psend_index ) {
	init_index_table(psend_index);
	free(psend_index);
    }
    if( psend_color )
	free(psend_color);
    if( pcolorno_table_record )
	free(pcolorno_table_record);

    return(ERROR);
これで、
    if( irtn != NORMAL )
        {
		freeの連続技
        }
は、単に、
    if( irtn != NORMAL )
	goto error;
と書くだけですんでしまいます。忘れてならないことは、最初に、これらのポ インタを必ずNULLに初期化しなければならないことです。コンパイラによって は、局所変数を全て0(NULL)に初期化するものもあり、この辺りはソフトウェ アの移植のとき、マシンの差がでてきます。暗黙のマシン依存の初期化などに 頼らず、自分でちゃんと初期化してしまえば、何も問題はないでしょう。


Copyright1996 Hirofumi Fujiwara. No reproduction or republication without written permission
『Cプログラミング診断室』目次次(第10章 最長不倒関数 いやらしい個所)