在发布模式中,代码行为与预期的不同

下面的代码在调试模式和发布模式下生成不同的结果 (使用 VisualStudio2008) :

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

在 VisualStudio 的发布模式下选择“优化-> 不优化”,输出结果将是正确的。然而,我想知道为什么优化过程可能导致错误的输出。


更新:

正如 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
10522 次浏览

假设您报告的事实是正确的,这将是一个编译器错误。检查编译器的最新版本。如果错误仍然存在,提交一份错误报告。

这很有趣,至少从历史的角度来看是这样的。我可以重现 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   :        }

更新 : 我在2008年之前安装的所有版本的 VC 都有相同的 bug,除了 VC6——编译程序会让 VC6编译器崩溃:

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

因此,这是一个错误,持续在 MSVC 的形式或其他超过10年!