C言語でsignedとunsignedの比較
C言語でsignedとunsignedを比較するとプログラマが意図しない結果が返る場合があります。
試験プログラム: ファイルa.c
#include <stdio.h> int main(int argc, char *argv[]) { int a = -1; unsigned int b = 1; short c = -1; unsigned short d = 1; if (a < b) printf("int: -1 < 1\n"); else printf("int: -1 >= 1\n"); if (c < d) printf("short: -1 < 1\n"); else printf("short: -1 >= 1\n"); return 0; }
実行例: (環境は、WindowsXP、PentiumM、gcc(cygwin))
$ gcc --version gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125) Copyright (C) 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc a.c $ ./a.exe int: -1 >= 1 … -1の方が1より大きいって返ってくる。 short: -1 < 1 … こちらは問題ない。
gccでは-Wsign-compareを付けることで、プログラマが意図しない結果を返す場合に警告を表示することができます。このオプションは-Wallには含まれていません。-Wallとは別途指定する必要があります。
$ gcc -Wsign-compare a.c a.c: In function `main': a.c:11: warning: comparison between signed and unsigned
さて、intの場合はおかしな結果になってshortの場合は問題ないのはなぜでしょう。と思ってアセンブラを確認してみました。
_main: … movl $-1, -4(%ebp) … int a = -1; movl $1, -8(%ebp) … unsigned int b = 1; movw $-1, -10(%ebp) … short c = -1; movw $1, -12(%ebp) … unsigned short d = 1; movl -4(%ebp), %eax cmpl -8(%ebp), %eax … if (a < b) jae L2 … 符号なし整数比較結果ジャンプ命令 movl $LC0, (%esp) call _printf jmp L3 L2: movl $LC1, (%esp) call _printf L3: movswl -10(%ebp),%edx movzwl -12(%ebp), %eax cmpl %eax, %edx … if (c < d) jge L4 … 符号あり整数比較結果ジャンプ命令 movl $LC2, (%esp) call _printf jmp L5 L4: movl $LC3, (%esp) call _printf L5: movl $0, %eax leave ret
shortの場合は、intとして(符号ありで)比較しています。このためプログラマの意図通りの結果が返っているようです。
shortの場合にintにしてから比較っていうのは、C言語の仕様なのでしょうか? んー、調べきれていません… 識者の方、教えてください。