彷徨えるフジワラ

年がら年中さまよってます

DTrace で const 指定が無視される件 - その2

色々弄繰り回しているうちに、はて const 修飾って言語仕様上どうなってたっけ?という根源的な問題に突き当たる。

hoge(const char* v1[],
     char const* v2[],
     char* const v3[])

上記の様に宣言された場合、コンパイラによって禁止される各引数へのアクセスは、v1 に関しては余裕でわかるけど、他の奴は全然予想と違う結果に。
っつーか、配列形式の宣言はよくわかんねー!ということで、ポインタ形式に展開してみる。

hoge(const char** v1,
     char const** v2,
     char* const* v3,
     char** const v4)

で、個々の const 修飾については:

  • "char" に対する const 修飾なので、"v1[0][0] = 'a'" は禁止
  • "char" への "*" に対する const 修飾なのだけど、何が禁止?
  • "char*" への "*" に対する const 修飾なので、"v3[0] = NULL" は禁止
  • v4 に対する const 修飾なので、"v4 = NULL" が禁止

じゃなかろうか?ということで、コンパイラを通してみると.... おぉ!v1, v3, v4 に関しては合っている!ってーことで、配列形式の v3 における const の修飾先は、引数名 v3 だと思っていたけど、実は配列っつーかポインタに掛かっているのね。

それと、v2 は v1 と同じ扱いだった。

実は、CTF ライブラリを直接叩いて色々やってみたところ、"char*" を const 修飾した型の文字列化では、"char const*" じゃなくて "char *const" が得られたので、「????」になっていたのだが、繋がって来た感じがする。

これまでの結果を踏まえつつ、ウンウン唸る事暫し。

これまでは、他の修飾要素(signed とか)と同様に、const は後ろに続く要素を修飾するものと考えていたのだけれど、どうも「先行要素を修飾する」と考えた方が辻褄があっている気がしてきた。

  • "char const**" では "char" を修飾 → ** の先の改変が禁止
  • "char* const*" では "char*" を修飾 → * の先の改変が禁止
  • "char** const" では "char**" を修飾 → 変数そのものの改変が禁止

でもって、"const char**" は特殊ケースというか過去の亡霊というか、広く使われちゃっているので "char" を修飾するものとみなそう、ということだと考えると、比較的すっきりする感じ。

こういった細かいところが曖昧だったりするので、やっぱり ANSI の言語仕様を引っ張ってこないと駄目だなぁ、ということで、ISO の C 言語ワーキンググループのページから C99 の言語仕様を調達して確認。

....なんかこの仕様、C89 とか C95 仕様を前提に書いているっぽいなぁ。厳密な解釈に関しては微妙に記述を避けてるような気が。まぁ、いいか。

ついでと言ってはなんだけど、以下の記述が等価らしいことを知った。っつーか、"[const]" なんて記法が通ることも知らなかったよ .... orz

hoge(char** const v);
hoge(char* v[const]);

人生日々勉強だなぁ。