GCCの評価順序

ひょっとしたら知っている人には当たり前かもしれませんが…

C言語の関数引数の評価順序といえば未規定動作*1の典型ですが、学生さんとの研究の関係で確認したら、GCC

#include <stdio.h>
void foo(int x, int y, int z)
{
}
int main() {
    foo(printf("1"),printf("2"),printf("3"));
    return 0;
}

を実行すると、本当に

> gcc -Wall -O0 hoge.c -o hoge
> ./hoge
321> gcc -Wall -O3 hoge.c -o hoge
> ./hoge
123> gcc -v
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.6/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.6 20060404 (Red Hat 3.4.6-9)

となることを発見(GCCのバージョンが古いのは気にしないでください)。いや、「だから何?」と言われても困るのですが。

P.S. やはりGCCのバージョンや環境にもよるらしい。すごい。

> gcc -Wall -O0 hoge.c -o hoge
> ./hoge
321> gcc -Wall -O3 hoge.c -o hoge
> ./hoge
321> gcc -v
Using built-in specs.
Target: x86_64-unknown-linux-gnu
コンフィグオプション: ./configure 
スレッドモデル: posix
gcc バージョン 4.2.2
> gcc -Wall -O0 hoge.c -o hoge
> ./hoge
123> gcc -Wall -O3 hoge.c -o hoge
> ./hoge
123> gcc -v
Reading specs from /usr/local/lib/gcc/sparc-sun-solaris2.8/3.4.2/specs
Configured with: ../gcc-3.4.2/configure --prefix=/usr/local --program-suffix=-3.4.2 --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --disable-nls
Thread model: posix
gcc version 3.4.2

*1:未規定動作(unspecified behavior) ≠ 未定義動作(undefined behavior)。大ざっぱに言うと、前者はあくまで「まともなプログラム」の動作だが、一通りに決まっていない、というだけ。後者は「バグったプログラム」の動作で、segmentation faultでもセキュリティホールでも、何でも起こりうる。正確にはISO規格をば。