Unix/Linuxプログラミング 理論と実践 第3章(研究課題)

備忘録です.
環境:FreeBSD 8.2-STABLE
書籍:Unix/Linuxプログラミング理論と実践

3.1の話.dirent構造体のd_nameの長さについて.

各環境でのd_nameの長さ

FreeBSD 8.2-Rで,d_nameを含むdirent構造体の定義は/usr/include/sys/dirent.hにあり,d_nameの長さは256.*1
Linuxでもd_nameの長さは256のようだ.((http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/readdir.3.html))
ところが,Solaris(SunOS)ではd_name
の長さは1となっているらしい.*2

d_nameの確保方法いろいろ

d_nameに入るファイル名の長さは,ファイルによって異なるため可変長と言える.
構造体中で可変長の文字列(配列)を扱うために,先人達がいくつもの方法を考えている.
ここでは,dirent構造体へのポインタ変数,struct dirent *dsを初期化する場合を考える.

1. d_nameを固定長の配列として定義する(1)
struct dirent {
/* 省略 */
	char	d_name[256];
};

この場合,dirent構造体を動的に確保すると次の処理になる.

  1. dpを動的に確保(malloc()など)
  2. d_nameにファイル名を格納

d_name配列に入るファイル名が255文字であることは少ないため,使わない領域が無駄にはなる.

2. d_nameをchar*として定義する
struct dirent {
/* 省略 */
	char	*d_name;
};

この場合,dirent構造体を動的に確保すると,次の処理になる.

  1. dpを動的に確保(malloc()など)
  2. d_nameを動的に確保(malloc())

使う領域だけを確保するため無駄はないが,malloc()が2回呼ばれるオーバヘッドがある.

3. d_nameをchar d_name[1]と定義する
struct dirent {
/* 省略 */
	char	d_name[1];
};

'\0'で1文字消費するため,要素数が1では'\0'しか入らない..と思ってしまうが,どうするのだろうか.
この場合,dirent構造体を動的に確保すると,次の処理になる.

  1. dpを動的に確保(malloc()など),このとき確保するサイズをsizeof(dp)+strlen(d_nameに格納したい文字列)とする
  2. d_nameに文字列をコピーする(strcpy()など)

メモリは必要な量しか確保しないし,malloc()は1回しか呼ばない.
境界チェックを行なわないことを前提としたテクニック.
構造体の最後のメンバにのみ利用できる.
dirent構造体の定義では,d_nameは要素数が1なのに,それ以降の領域にもアクセスしているのが気になるところ.

4. d_nameを固定長の配列として定義する(2)
struct dirent {
/* 省略 */
	char	d_name[256];
};

この場合,dirent構造体を動的に確保すると次の処理になる.

  1. dpを動的に確保(malloc()など),このとき確保するサイズをsizeof(dp)-(255-strlen(d_nameに格納したい文字列))とする
  2. d_nameにファイル名を格納

d_nameで実際に使う領域分までしかdirent構造体用の領域を確保しない.
3と比べると,dirent構造体の定義にあるd_nameの要素数を超える領域にはアクセスしないように見える.
しかし,もしd_nameに格納したい文字列が255を超えていれば*3,3と同様のことが発生する.
また,d_nameの定義では256個の要素があるように見えるのに,実際にはそれ以下の領域しか確保していないため,存在しない領域へのアクセスを起こしやすいという点で3よりも危険かもしれない.

5. d_nameをchar d_name[]と定義する
struct dirent {
/* 省略 */
	char	d_name[];
};

この場合,dirent構造体を動的に確保すると次の処理になる.

  1. dpを動的に確保(malloc()など),このとき確保するサイズをsizeof(dp)+strlen(d_nameに格納したい文字列)-1とする
  2. d_nameに文字列をコピーする(strcpy()など)

長さ0の配列を定義する方法は,C99から導入された方法で,構造体の最後のメンバに限って利用できる.
C99より前の規格では,内の長さの指定を省略することを認めていない((char s = "hoge";は可能)).
規格として正式に認められた記法のため良い方法に思えるが,C99に対応していない環境ではコンパイルできないという互換性の問題あり.

FreeBSDでは?

FreeBSDで,1と4のどちらかの方法あるいは別の方法を用いているのかは不明.

*1:__BSD_VISIBLEは,/usr/include/sys/cdefs.h参照.

*2:http://h21007.www2.hp.com/portal/download/files/unprot/stk/solaris_stk/impacts/i182.html

*3:UNIX系OSで使用する多くのファイルシステムでは,最大ファイル名長は255となっている