なんとなく悩まされたので悩んだ過程と私なりの結論とかを書いてみます。 考察なんで嘘書いてます。(言いきってどうする)。 嘘発見情報や追加情報などありましたらぜひご連絡ください。
広い考察をしたいくせに確認できるプラットホームがあまりに少ないのが 問題です。補完できるときに補完したい...
ここで「問題」としているもののは、そもそもそういうふうに使われることを 意図して作られていないものを無理に使っているから、と言えるような 気もしますが...
まずはMSDN Libraryの解説をごらんください。
この定数はstdlib.hにあると書いてあります。 確かに、
MSDN Libraryでは、「Microsoft独自の仕様」の場合は そのことを明記しているわけですが、ここで出てくる定数には そのような表記はありません...謎だ。アンダースコアがつくということは DOS時代のファンクションリクエストの仕様をひっぱってるのか?
とりあえずそれがどのような値を持つのか、実際に表示させてみました。 私の環境(Windows2000 SP1)では
他にもFILENAME_MAX(@stdio.h) というものがあります。手持ちの環境では... まずはFreeBSDと NetBSD
stdio.h:#define FILENAME_MAX 1024 /* must be >= PATH_MAX <sys/syslimits.h> */続いてMingw32
* * The maximum length of a file name. You should use GetVolumeInformation * instead of this constant. But hey, this works. * * NOTE: This is used in the structure _finddata_t (see io.h) so changing it * is probably not a good idea. */ #define FILENAME_MAX (260)Cygwin
#ifdef __FILENAME_MAX__ #define FILENAME_MAX __FILENAME_MAX__ #else #define FILENAME_MAX 1024 #endif最後にVC++
#define FILENAME_MAX 260などということになっています。
あとは...FreeBSDやNetBSDでは、各種MIN/MAXの定数は /usr/include/limits.hから辿れるところにまとめられているのですが、
(NetBSD,FreeBSD) #define _POSIX_PATH_MAX 255 (Cygwin) #define PATH_MAX (260 - 1 /*NUL*/) (VC++) #define _POSIX_PATH_MAX 255 (Mingw32) 該当なし... そのかわり(?) * * File system limits * * TODO: NAME_MAX and OPEN_MAX are file system limits or not? Are they the * same as FILENAME_MAX and FOPEN_MAX from stdio.h? * NOTE: Apparently the actual size of PATH_MAX is 260, but a space is * required for the NUL. TODO: Test? */ #define PATH_MAX (259)VC++は_POSIX_が定義されているときだけ出現するようです。 NetBSD/FreeBSDでは_ANSI_SOURCEが定義されているときには 出現しないようです。
ちょっと手元のマシン(FreeBSD 4.3-RELEASE)をいじめてみました。(ごめんよ)
% cd /usr/src && find -name '*.[ch]' -exec grep -l -H FILENAME_MAX \{\} \; ./bin/sh/main.c ./contrib/less/lsystem.c ./contrib/libio/stdio/stdio.h ./gnu/lib/libstdc++/_G_config.h ./gnu/usr.bin/man/man/man.c ./include/stdio.h ./lib/libc/stdtime/localtime.c ./lib/libc/stdtime/private.h ./lib/libdisk/create_chunk.c ./release/sysinstall/command.c ./release/sysinstall/config.c ./release/sysinstall/devices.c ./release/sysinstall/disks.c ./release/sysinstall/dmenu.c ./release/sysinstall/install.c ./release/sysinstall/installUpgrade.c ./release/sysinstall/label.c ./release/sysinstall/misc.c ./release/sysinstall/msg.c ./release/sysinstall/package.c ./release/sysinstall/sysinstall.h ./release/sysinstall/system.c ./usr.sbin/btxld/btxld.c ./usr.sbin/i4b/isdnd/isdnd.h ./usr.sbin/i4b/isdnd/monitor.c ./usr.sbin/pkg_install/add/extract.c ./usr.sbin/pkg_install/add/main.c ./usr.sbin/pkg_install/add/perform.c ./usr.sbin/pkg_install/create/main.c ./usr.sbin/pkg_install/create/perform.c ./usr.sbin/pkg_install/create/pl.c ./usr.sbin/pkg_install/delete/perform.c ./usr.sbin/pkg_install/info/main.c ./usr.sbin/pkg_install/info/perform.c ./usr.sbin/pkg_install/info/show.c ./usr.sbin/pkg_install/lib/file.c ./usr.sbin/pkg_install/lib/pen.c ./usr.sbin/pkg_install/lib/plist.c ./usr.sbin/pkg_install/lib/str.c ./usr.sbin/pkg_install/lib/deps.c以外というか予想通りというか...あまり使われてませんな。 次は_POSIX_PATH_MAX
find . -name "*.[ch]" -exec grep -l -H _POSIX_PATH_MAX \{\} \; ./contrib/cvs/lib/system.h ./contrib/ncurses/ncurses/curses.priv.h ./contrib/ncurses/progs/progs.priv.h ./gnu/usr.bin/rcs/lib/rcsbase.h ./gnu/usr.bin/tar/pathmax.h ./include/limits.h ./lib/libc/gen/getcap.c ./crypto/heimdal/lib/roken/getcap.c ./crypto/kerberosIV/lib/roken/getcap.c
えーと次にすることはどんな風に使われてるのか、使われてないところは どんな風に処理してるのか、の検証、ですよね。時間できたらやります(……)
その定数がどういう意図で設定されているのかを理解せずに いい加減な使いかたをすると哀しいことになりそうです。 例えばMAX_PATHを「ファイル名の長さ」のMAXに使ってみたり。
また、たまたま望んでいた値に近いからといって、 その定数の適用範囲を超えた使用もよく考えたほうがよいでしょう。 stdio.hに宣言されている定数を、stdio以外の用途で使うとか。 いずれにしろ「意味を把握する」というところで総括できるかなと思います。
すでに見てきたように、MAX_PATHやFILENAME_MAXの指す値は 環境に依存しますし、同一系列の環境でも今後変わらないという保証はありません。
前項とかぶりますが...
これらは「定数」ですから値はコンパイル時に決定されます。 コンパイル時に決まる定数ということは、それをコンパイルした環境では 正しい値でも他のところに持っていって正しいとは限りません(コンパイル しなおせば別だが)。
というと言いすぎかもしれませんが...
以上のようなことで、これらの定数は主にライブラリやAPIが 内部的に利用するものなのではないかと私は考えているのですが、 実際には、たとえばWindowsではMAX_PATHを超えた長さの パスを許すようなAPIが出ています。 FindFirstFileみたいな 関数とか。
ただでさえプラットホーム依存な上に、互換性問題とかで すでに定数の意味を果たせなくなっているものがあるように 思います。そういうものを信じて使っては泣いてしまうことになりそうです。
以上の項を考えると以下のような結論が出ると思います。 なんというか、当たり前のことをわざわざ勿体つけて 書いてるような気もしますが。
例えばファイルにパス名をセーブするときに、ファイル名が入る フィールド長をMAX_PATH固定長にする、といった使いかたはしないほうがよい でしょう。移植性がないばかりか、目の前ではきちんと動いているのに 他の場所でおかしいという最悪の部類のエラーを呼びこんで泣けます。 面倒でも可変長にするとかの手を考えましょう。
FILENAME_MAXやMAX_PATHが最大長として信用できないことは 今まで述べてきた通りです。よって言うまでもないことですが bound checkを怠らないでください。これ以上の長さの 文字が来るということは、たとえそれがユーザからの 入力ではなくても、ありうることです。
MAX_???系の例を見て分かる通り(またMSDN Libraryにも 明記されている通り)、 絶対パスの最大長(_MAX_PATH)と、それを構成する要素(_MAX_DRIVE、 _MAX_EXT、_MAX_FNAME、_MAX_PATH)などの最大長の和とを 比べると後者の方が全然大きいです。 というわけで、これらの定数を使って絶対パスを合成するときは、 その長さが_MAX_PATHを超えないように注意しないと 前項の問題を自分から呼び込むことになります。
これらの定数は、
char filename[FILENAME_MAX];みたいな使われかたをすることが多いようですが、 実際問題、それですべてが丸くおさまるわけではなさそうです。
つまり、
後者の可変長の方ですが、Cだとmallocなどで確保したメモリを 誰の責任で解放するのかという問題があります。 誰もfreeしなければメモリリーク(の原因)になります。逆に二重にfreeして 落ちることもあるかもしれません。 (確保されたメモリが本当にmallocで確保されたものなのか、ということは 外からでは判断つきづらいという問題もあります)
可変長の文字列を渡すための方法としてよく見られるのは、
具体的なやりかたとしては
↑で「NULLでもよい」「0でもよい」と書いたのは、NULLと0(の入った変数の アドレス)を渡して先に必要な長さを貰ってきてからメモリを用意するという手が 使えるという意味です。
そもそもいつこういう定数が生まれたのか、どういう意図で生まれて どういう使われかたをしたのか、知らなきゃいけないことがやまもりです。
一生懸命調べればいろいろ勉強になるし、それを書けばきっと 楽しいドキュメントが書けると思ったが... なんかあれだ(どれや)