読者です 読者をやめる 読者になる 読者になる

可変長バッファの実装

ご存知のように、C/C++ の「配列」は、コンパイル時に (静的に) そのサイズを決定する必要があります。
そのため、動的配列を扱うには、C では malloc/free 系の関数、C++ では new/delete[] 演算子を利用することになるわけですが、

  • コードが 複雑になる
  • 解放の手続きが面倒 (忘れるとメモリリークが発生)

といった理由から、静的な配列 (固定長) が入出力用のバッファとして使用されてしまうことが少なくありません。 しかし、こうした横着はバッファオーバーラン (buffer overrun)というセキュリティ上の問題 (脆弱性) を引き起こします。

バッファオーバーラン発生の原因は、固定長バッファの仕様、即ち、入力データサイズに対する (不適切な) 上限の仮定にあります。
最近作られた言語では、可変長バッファを利用した入出力機構を (言語使用あるいは標準ライブラリとして) 備えているため、こうした問題が起こることは少ないようです。

それなら、C/C++ でも同じ機構を作ってそれを使えばいいじゃない。というわけで、早速実装してみました。 以下に示したのは C++ での実装ですが、C であれば new/delete [] 演算子malloc, free に置き換えればOKです。

#include <stdio.h>
#include <string.h>
#define INIT_BUF_SIZE 32
//ストリームからのテキスト読み込み
char* LoadText(FILE* fp){
if (!fp) return NULL;
int   nSize =INIT_BUF_SIZE;
char* lpac  =new char [nSize + 1];
int nCount =0;
for (;;){
//読み込み
int nRes =fread(lpac + nCount, 1, nSize - nCount, fp);
if (nRes < nSize - nCount){
nCount +=nRes;
break;
}
nCount +=nRes;
//バッファ拡張
nSize  *=2;
char* lpacNew =new char [nSize + 1];
memcpy(lpacNew, lpac, nCount);
delete [] lpac;
lpac =lpacNew;
}
//文字列終端を表すヌル文字 (\0) を付加
lpac[nCount] ='\0';
return lpac;
}

この LoadText 関数を使う際は、戻り値が指す領域を使用後に delete [] (free) で解放することを忘れないで下さい。 C++ であれば、戻り値を char* を保持するクラス型にして、デストラクタに解放を任せるのがスマートでしょう。


担当: 成田 (くれぐれも gets は使わないように。)