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


131

以下代码在调试模式和发布模式下(使用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
这看起来像是一个编译器错误(也是一个相当重要的错误)。
WhozCraig

1
@WhozCraig只更新i * 16帖子中的输出,结果是正确的。
罗里斯·林2015年

4
@juanchopanza:从我对MS和VS的错误修正的经验来看,他们在得知错误后会修复此类错误,但不要将这些修正应用于较旧的VS,因此,如果由于某种原因而被迫使用旧版本的VS VS,然后一个人会遇到此类错误,直到可以将其升级到新版本为止。
Kaiserludi

2
FWIW可以在即将到来的Visual Studio 2015中正常运行
ismail

Answers:


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)相当老,并且修复程序已经存在了几年,所以我认为除了使用较新的编译器之外,您不会期望Microsoft采取任何措施(尽管也许有人会建议解决方法)。

问题在于,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年!


如果我对x86组装时间的记忆是正确的,那么与esi而不是eax进行比较的原因是comp eax,255会导致管道停顿,因为eax刚刚被编写。
洛伦·佩希特尔

3
我的猜测(转换):结果> 255,结果/ 16> 255/16,i> 15,i <= 14
teki

很有意思!同样,如果您将比较从更改为result > 255result >= 255则其行为也正确。在VS2010的更改cmp esi, 14cmp esi, 16(和jlejl)。
opello

16

假设您所报告的事实是正确的,则可能是编译器错误。检查最新版本的编译器。如果错误仍然存​​在,请提交错误报告。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.