a == b == c

ふと目にしたCのコードでa == b == c と書いてある箇所を目にしてこれコンパイルできるのかなと思ってやってみるとコンパイルできたので見てみた。

yoshimi% cat test.c
#include <stdio.h>

int main ()
{
  int a, b, c;
  a = 0;
  b = 0;
  c = 0;

  if ( a == b == c ) {
    printf("passed!!\n");
  } else {
    printf("not passed!!\n");
  }
}
yoshimi% cat test2.c                                                                                                                                                                          
#include <stdio.h>

int main ()
{
  int a, b, c;
  a = 0;
  b = 0;
  c = 0;

  if ( a == b && b == c ) {
    printf("passed!!\n");
  } else {
    printf("not passed!!\n");
  }
}

test2はpassするが、testは通らないので、 優先順位として、( a == b ) == cと解釈されてるっぽい。

アセンブラにしてみるとどうだろうということで見てみた。ちなみに、gcc -S test.cでtest.sという名前でアセンブラにしてくれるらしい。diffを取ってみるとわかりやすい。

yoshimi% diff -u test*.s               
--- test.s      2009-03-31 01:43:33.000000000 +0900
+++ test2.s     2009-03-31 01:43:20.000000000 +0900
@@ -10,28 +10,28 @@
        movl    %esp, %ebp
        pushl   %ebx
        subl    $36, %esp
-       call    L7
+       call    L8
 "L00000000001$pb":
-L7:
+L8:
        popl    %ebx
        movl    $0, -20(%ebp)
        movl    $0, -16(%ebp)
        movl    $0, -12(%ebp)
        movl    -20(%ebp), %eax
        cmpl    -16(%ebp), %eax
-       sete    %al
-       movzbl  %al, %eax
+       jne     L2
+       movl    -16(%ebp), %eax
        cmpl    -12(%ebp), %eax
        jne     L2
        leal    LC0-"L00000000001$pb"(%ebx), %eax
        movl    %eax, (%esp)
        call    L_puts$stub
-       jmp     L6
+       jmp     L7
 L2:
        leal    LC1-"L00000000001$pb"(%ebx), %eax
        movl    %eax, (%esp)
        call    L_puts$stub
-L6:
+L7:
        addl    $36, %esp
        popl    %ebx
        leave

アセンブラはまじめに勉強したことがないのでほとんど読めないですが、これぐらいの量なら頑張れそうなので、勉強してみるかなぁ。

一応どういう挙動になるなのかの予測はついてるのでそれに基づいて各命令の意味を調べてくだけでもいい勉強にはなりそう。

test.cを以下のように変更して、アセンブラのコードでdiffをとってみると、

yoshimi% diff -u test.c test3.c  
--- test.c      2009-03-31 01:32:39.000000000 +0900
+++ test3.c     2009-03-31 02:19:50.000000000 +0900
@@ -7,7 +7,7 @@
   b = 0;
   c = 0;
 
-  if ( a == b == c ) {
+  if ( ( a == b )  == c ) {
     printf("passed!!\n");
   } else {
     printf("not passed!!\n");

差がないので、とりあえず予測が正しいということは裏付けがいちおう取れたっぽい。

もちろん-Wallでコンパイルすれば以下のように警告がちゃんと出るので、

test.c:10: warning: comparisons like X<=Y<=Z do not have their mathematical meaning

まぁふつうはこんな書き方されないですよね。きっと。