X[Y] と Y[X] は異なる。

注:以下の話は間違いでした(下の方に訂正書きます)

今日見つけた自作コンパイラのバグ。型検査時に

整数[配列]

のパターンを

配列[整数]

の形に直してしまっていた。
a[i]とi[a]が同じというのはC言語では有名な仕様だけれども,オペランドの評価順序の問題があるので実際には同じではない。


同じようなミスで,

(整数) + (ポインタ)

(ポインタ) + (整数)

に直すなどをしてしまっていた。
実際にはこれらの入れ替えは正規化をしてオペランドの副作用を除去してからでなければならない。

訂正

上で書いたのは間違いだった。
まずC言語ではオペランドの評価順序は基本的に処理系依存であるという事を忘れていた。


しかし,X[Y]とY[X]が同じかどうかも処理系依存になるのかというと, どうやらそうではないらしい。
まず規格には「(P)+N (equivalently, N+(P)」とあるのでequivalentの意味を間違っていなければ

(ポインタ) + (整数)
(整数) + (ポインタ)

の意味は同じでなければならない。つまり, もし上の式を左->右と評価するならば, 下の式は右->左と評価しなければならない。

そして, 「E1[E2] is identical to (* ((E1) + (E2)))」とあるので,(E1) + (E2)の部分に上と同じ規則が使用されるわけだから,

a[i]
i[a]

のどちらも評価順序が同じでなければならない。上の式がa->iと評価するならば, 下の式もa->iと評価しなければならない。

つまり, 「構文が同じでも型に依存して評価順序が変わる」という奇妙な現象がおきるらしい。

実際にGCCで実験してみたところ,

(puts("foo"), 1) + (puts("bar"), 0);

foo
bar

と出力されるが,

(puts("foo"), 1) + (puts("bar"), (void *)0);

bar
foo

となった。

ということで, 自分がバグだと思ったものはたまたまバグではなかったわけだが, 仕様を全然わかってなかったという事で反省。