奇怪的C整数不等式比较结果
#include #include int main() { long ival = 0; printf("ival: %li, min: %i, max: %i, too big: %i, too small: %i\n", ival, INT_MIN, INT_MAX, ival > INT_MAX, ival < INT_MIN); }
这给出了输出:
ival: 0, min: -2147483648, max: 2147483647, too big: 0, too small: 1
怎么可能?
(我实际上在getargs.c
: getargs.c
中getargs.c
了CPython 2.7.3中的这个问题/错误。如果你查找代码, case 'i'
,那么检查ival < INT_MIN
对我来说总是如此。还有测试用例源以及进一步的参考 。)
好吧,我现在测试了几个不同的编译器。 为x86编译的GCC / Clang都返回预期的(太小:0)。 当编译为armv7时,意外输出来自Xcode工具链中的Clang。
如果要重现:
这是确切的编译命令: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk test-int.c
这是Xcode 4.3.2。
我将生成的a.out
复制到我的iPhone上并执行了它。
如果有人对由此生成的汇编程序代码感兴趣:
.section __TEXT,__text,regular,pure_instructions .section __TEXT,__textcoal_nt,coalesced,pure_instructions .section __TEXT,__const_coal,coalesced .section __TEXT,__picsymbolstub4,symbol_stubs,none,16 .section __TEXT,__StaticInit,regular,pure_instructions .syntax unified .section __TEXT,__text,regular,pure_instructions .globl _main .align 2 .code 16 .thumb_func _main _main: push {r7, lr} mov r7, sp sub sp, #20 movw r0, #65535 movt r0, #32767 movs r1, #0 movt r1, #0 str r1, [sp, #16] str r1, [sp, #12] ldr r1, [sp, #12] ldr r2, [sp, #12] cmp r2, r0 movw r0, #0 it gt movgt r0, #1 and r0, r0, #1 ldr r2, [sp, #12] cmn.w r2, #-2147483648 movw r2, #0 it lt movlt r2, #1 and r2, r2, #1 mov r3, sp str r2, [r3, #4] str r0, [r3] mov.w r2, #-2147483648 mvn r3, #-2147483648 movw r0, :lower16:(L_.str-(LPC0_0+4)) movt r0, :upper16:(L_.str-(LPC0_0+4)) LPC0_0: add r0, pc blx _printf ldr r1, [sp, #16] str r0, [sp, #8] mov r0, r1 add sp, #20 pop {r7, pc} .section __TEXT,__cstring,cstring_literals L_.str: .asciz "ival: %li, min: %i, max: %i, too big: %i, too small: %i\n" .subsections_via_symbols
这是一个错误。 C标准中没有空间,因为它too small
而不是0以外的任何东西。这是它的工作原理:
-
由于
INT_MIN
是一个int
,因此在“通常的算术转换”期间它会转换为long
。 发生这种情况是因为long
排名高于int
(两者都是签名类型)。 由于所有操作数至少具有int
等级,因此不会发生促销。 未调用未定义或实现指定的行为。 -
在转换期间,保留
INT_MIN
的值。 由于它从int
转换为long
,并且保证long
至少具有int
的范围,因此在转换期间必须保留INT_MIN
的值。 未调用未定义或实现指定的行为。 不允许进行模块化转换,仅适用于无符号类型。 -
比较结果应为
0
。
标志延长或其他此类事物没有摆动空间。 此外,由于对printf
的调用是正确的,因此没有问题。
如果您可以在其他系统上重现它,或将其发送给可以重现它的其他人,您应该直接向您的工具链供应商报告该错误。
尝试重现该错误:我无法重现以下任何组合的行为,所有这两种组合都具有优化开启和关闭:
- GCC 4.0,PPC + PPC64
- GCC 4.2,PPC + PPC64
- GCC 4.3,x64
- GCC 4.4,x64
- Clang 3.0,x64
这打印什么?
#include printf("%016ld\n", LONG_MAX); long l_int_min = (long)INT_MIN; printf("%016lx\n", l_int_min);
我想知道INT_MIN
是否在不进行符号扩展的情况下被强制执行。 这将使0小于结果值。
编辑:好的,第一个printf()
结果是0000002147483647
,这意味着该平台上的long
是32位,就像int
一样。 因此,将int
转换为long
应该不会改变任何东西。
我试图保留“这是一个编译器错误”作为最后的手段,但这对我来说看起来像编译器错误。