この機能はまともに動いてほしい

GCCもどんどんバージョンアップして、今では4.6.0が最新版になっています。その過程で、C++0Xや(少しだけですが)C1Xへの対応も進んでいるわけですが、本来であればとっくに対応できていなければならないはずの機能がいまいちだったりもします。

今回問題視したいのは、配列要素中のstaticです。GCCでまともに動かないので、この仕様を使っているプログラマはほとんどいないと思います。そこで、まずは仕様についておさらいしておくことにします。

関数の仮引数に配列型を指定した場合、C99では、[ ]の中にstaticキーワードを書くことができます。その場合、代入式を伴って、次のように指定することになります。

void f(int a[static 10]);

上のように書いた場合、aに対応する実引数は、10個以上の要素数を持つ配列の先頭要素を指していなければなりません。この仕様をうまく使えば、かなり安全性を強化できるはずです。たとえば、fgetsであれば、次のように書き換えることができることでしょう。

char *fgets(int n, char s[static n], FILE *stream);

引数の順序が変わってしまうのが難ですが、何なら次のようなマクロを定義してもよいかもしれません。

char *_Fgets(int n, char s[static n], FILE *stream);
#define fgets(s, n, f) _Fgets((s), (n), (f))

ところがGCCでは、いくら[ ]の中にstaticキーワードと最小限のサイズを指定しても、何のエラーチェックもしてくれません。明らかに要素数が不足している配列の先頭要素を渡しても、そして-pedanticや-Wallといったオプションを付けても、警告ひとつ出してくれません。

確かに未定義の動作なので、うんともすんともいわなかったとしても、それは仕方がないことなのですが、せめて警告のひとつぐらいは出してほしいとことです。

ただ、この仕様をまともに実装しようとすると、はたしてどう振る舞えばよいのか微妙なところもあります。たとえば、実引数が配列型ならよいのですが、ポインタで持ち回りしていた場合にはまともにエラーチェックができるとは思いません。そんなとき、判断が付かなければエラーにすべきなのかどうかがよくわかりません。

実引数の値は、「大きさを指定する式で指定される数以上の要素を持つ配列の先頭要素を指していなければならない」のであって、決して十分な大きさを持つ配列型でなければならないわけではありませんので。また、本当なサイズが足りないけれども、強引にキャストすれば不問になるのかもよくわかりません。たとえばこんな風にです。

void f(int [static 10]);
int a[3];
f(*(int(*)[10])&a);

といった具合に、どう実装すればよいのかよくわからないところはありますが、怪しいコードに対しては、せめて警告ぐらいは出してほしいところです。

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/904