在发布模式下,代码行为与预期不符。

132

以下代码在调试模式和发布模式下(使用Visual Studio 2008)会生成不同的结果:

int _tmain(int argc, _TCHAR* argv[])
{

    for( int i = 0; i < 17; i++ ) 
    { 
        int result = i * 16;

        if( result > 255 )
        {
            result = 255;
        }

        printf("i:%2d, result = %3d\n", i, result) ; 
    } 

    return 0;
}

调试模式的输出,如预期所示:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 240
i:16, result = 255

在发布模式下,当i:15时结果不正确:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 255
i:16, result = 255

在 Visual Studio 的发布模式下选择 "优化 -> 不优化",输出结果将是正确的。但我想知道为什么优化过程会导致错误的输出。


更新:

按照 Mohit JainBy 的建议,通过打印:

printf("i:%2d, result = %3d, i*16=%d\n", i, result, i*16) ;
释放模式的输出是正确的:
i: 0, result =   0, i*16=0
i: 1, result =  16, i*16=16
(...)
i:14, result = 224, i*16=224
i:15, result = 240, i*16=240
i:16, result = 255, i*16=256

15
看起来是编译器的一个 bug(而且相当严重)。 - WhozCraig
1
@WhozCraig 刚刚在帖子中更新了 i * 16 的输出结果,而且结果是正确的。 - Lorris Lin
4
根据我的经验,在处理微软(MS)和修复Visual Studio (VS)的Bug时,他们会在了解这些问题后进行修复,但不会将这些修复应用于旧版本的VS。因此,如果某人因某种原因被迫使用旧版本的VS,则只能一直面对这些错误,直到升级到新版本为止。 - Kaiserludi
2
顺便提一下,这在即将发布的 Visual Studio 2015 上运行良好。 - ismail
2个回答

115

从历史角度来看,这很有趣。我可以使用VC 2008(15.00.30729.01)和VC 2010(16.00.40219.01)(针对32位x86或64位x64)重现此问题。该问题不会出现在任何编译器中,我已尝试使用VC 2012(17.00.61030)开始。

我用于编译的命令:cl /Ox vc15-bug.cpp /FAsc

由于VC 2008(和2010)相当老旧,并且已经修复了几年,因此我认为您不能期望微软采取任何行动,除非使用更新的编译器(尽管也许有人可以建议解决方法)。

问题在于确定值是否应该强制为 255 的测试是基于循环计数而不是实际的 i * 16 表达式结果进行的。编译器仅仅是在应该开始强制将该值设为 255 的计数时出现错误。我不知道为什么会发生这种情况 - 这只是我看到的效果:

; 6    :    for( int i = 0; i < 17; i++ ) 

  00001 33 f6        xor     esi, esi
$LL4@main:
  00003 8b c6        mov     eax, esi
  00005 c1 e0 04     shl     eax, 4

; 7    :    { 
; 8    :        int result = i * 16;
; 9    : 
; 10   :        if( result > 255 )

  // the value `esi` is compared with in the following line should be 15!
  00008 83 fe 0e     cmp     esi, 14            ; 0000000eH
  0000b 7e 05        jle     SHORT $LN1@main

; 11   :        {
; 12   :            result = 255;

  0000d b8 ff 00 00 00   mov     eax, 255       ; 000000ffH
$LN1@main:

; 13   :        }

更新: 我先前安装的所有低于VC 2008版本的VC都有同样的错误,除了VC6编译程序会导致VC6编译器崩溃。

vc15-bug.cpp(10) : fatal error C1001: INTERNAL COMPILER ERROR

所以这是一个在 MSVC 中持续存在了 10 年以上的 bug!


如果我对x86汇编计时的记忆是正确的,那么与其使用eax进行比较,使用esi进行比较的原因是comp eax, 255会导致管道停顿,因为eax刚刚被写入。 - Loren Pechtel
3
我的猜测(转换): 结果 > 255, 结果/16 > 255/16, i > 15, i <= 14。 - teki
非常有趣!如果您将比较从result > 255更改为result >= 255,它会正确地运行。在VS2010中,这将把cmp esi,14更改为cmp esi,16(并将jle更改为jl)。 - opello

16
假设您提供的事实是正确的,那么这可能是一个编译器错误。检查最新版本的编译器。如果该错误仍然存在,请提交错误报告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接