Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么MSVC调试模式为一个空if()主体而不是另一个主体(i+;+;vs.+;+;i)省略了cmp/jcc?_C_Assembly_Visual C++_Reverse Engineering - Fatal编程技术网

为什么MSVC调试模式为一个空if()主体而不是另一个主体(i+;+;vs.+;+;i)省略了cmp/jcc?

为什么MSVC调试模式为一个空if()主体而不是另一个主体(i+;+;vs.+;+;i)省略了cmp/jcc?,c,assembly,visual-c++,reverse-engineering,C,Assembly,Visual C++,Reverse Engineering,我正在使用AMD64计算机(英特尔奔腾黄金4415U)比较从C语言转换的一些汇编指令(当然,确切地说,是反汇编) 对于Windows 10,我使用Visual Studio 2017(15.2)及其C编译器。 我的示例代码如下所示: int main() { int i = 0; if(++i == 4); if(i++ == 4); return 0; } 拆卸过程如下所示: mov eax,dword ptr [i] // if (++i

我正在使用AMD64计算机(英特尔奔腾黄金4415U)比较从C语言转换的一些汇编指令(当然,确切地说,是反汇编)

对于Windows 10,我使用Visual Studio 2017(15.2)及其C编译器。 我的示例代码如下所示:

int main() {
    int i = 0;
    if(++i == 4);
    if(i++ == 4);
    return 0;
}
拆卸过程如下所示:

mov         eax,dword ptr [i]  // if (++i == 4);
inc         eax  
mov         dword ptr [i],eax  

mov         eax,dword ptr [i]  // if (i++ == 4);
mov         dword ptr [rbp+0D4h],eax    ; save old i to a temporary
mov         eax,dword ptr [i]  
inc         eax  
mov         dword ptr [i],eax  
cmp         dword ptr [rbp+0D4h],4      ; compare with previous i
jne         main+51h (07FF7DDBF3601h)  
mov         dword ptr [rbp+0D8h],1  
jmp         main+5Bh (07FF7DDBF360Bh)  
*mov         dword ptr [rbp+0D8h],0
07FF7DDBF3601转到最后一行指令(*)。
07FF7DDBF360B转到“返回0;”

中,如果(++i==4)
,程序不会观察“added”i是否满足条件

但是,在
中,如果(i++==4)
,程序将“上一个”i保存到堆栈中,然后执行增量。之后,程序将“previous”i与常量整数4进行比较

导致两个C代码差异的原因是什么?它只是一个编译器的机制吗?更复杂的代码会有所不同吗?


我试图通过谷歌找到这一点,但是我没有找到差异的根源。我必须理解‘这只是一种编译器行为’?

就像Paul说的,该程序没有明显的副作用,并且在启用优化功能的MSVC或任何其他主要编译器(gcc/clang/ICC)的情况下,将
main
编译为
xor eax,eax
/
ret

i
的值永远不会逃逸函数(不会存储到全局或返回),因此可以完全优化它。即使是,恒定传播在这里也是微不足道的


MSVC的调试模式反优化代码生成器决定不在空的
if
主体上发出
cmp/jcc
,这只是一个奇怪的/实现细节;即使在调试模式下,这对调试也毫无帮助。它将是一条分支指令,跳转到它所经过的地址

调试模式代码的要点是,您可以单步遍历源代码行,并使用调试器修改C变量。并不是说asm是C到asm的字面和忠实的音译。(而且编译器可以快速生成,而无需在质量上花费任何精力,从而加快编辑/编译/运行周期。)

编译器的代码生成器到底有多死板并不取决于任何语言规则;没有实际的标准来定义编译器在调试模式下必须做的事情,比如对空
if
body实际使用分支指令


显然,对于您的编译器版本,
i++
post增量足以让编译器忘记循环体是空的

我无法用MSVC 19.0或19.10重现您的结果。(VS2015或VS2017)。或任何其他MSVC版本。我没有从MSVC、ICC或gcc获得任何条件分支

MSVC确实实现了
i++
,并为旧值实际存储到内存中,如您所示。太可怕了。GCC
-O0
可以显著提高调试模式代码的效率。当然,这仍然是相当愚蠢的,但在一个单一的声明中,它有时就不那么糟糕了

不过,我可以用叮当声重现它!(但它同时为
if
s分支):


正如Paul所说,该程序没有明显的副作用,在启用优化功能的MSVC或任何其他主要编译器(gcc/clang/ICC)的情况下,将
main
编译为简单的
xor eax、eax
/
ret

i
的值永远不会逃逸函数(不会存储到全局或返回),因此可以完全优化它。即使是,恒定传播在这里也是微不足道的


MSVC的调试模式反优化代码生成器决定不在空的
if
主体上发出
cmp/jcc
,这只是一个奇怪的/实现细节;即使在调试模式下,这对调试也毫无帮助。它将是一条分支指令,跳转到它所经过的地址

调试模式代码的要点是,您可以单步遍历源代码行,并使用调试器修改C变量。并不是说asm是C到asm的字面和忠实的音译。(而且编译器可以快速生成,而无需在质量上花费任何精力,从而加快编辑/编译/运行周期。)

编译器的代码生成器到底有多死板并不取决于任何语言规则;没有实际的标准来定义编译器在调试模式下必须做的事情,比如对空
if
body实际使用分支指令


显然,对于您的编译器版本,
i++
post增量足以让编译器忘记循环体是空的

我无法用MSVC 19.0或19.10重现您的结果。(VS2015或VS2017)。或任何其他MSVC版本。我没有从MSVC、ICC或gcc获得任何条件分支

MSVC确实实现了
i++
,并为旧值实际存储到内存中,如您所示。太可怕了。GCC
-O0
可以显著提高调试模式代码的效率。当然,这仍然是相当愚蠢的,但在一个单一的声明中,它有时就不那么糟糕了

不过,我可以用叮当声重现它!(但它同时为
if
s分支):


闻起来像优化,代码相当于
返回0其他任何东西都可以由编译器优化。@PaulOgilvie:除非它没有优化。这显然是未优化的编译器输出,它分别编译每个C语句,不进行任何常量传播,并且有效地将所有局部变量视为
易失性,因此如果调试器修改它们,程序仍然可以工作。当然,它仍然可以在语句中进行优化,并在空的
if
主体上删除跳转?你想从中获得什么样的见解?不太清楚他的意思。当然,他们的工作方式不同
# clang8.0 -O0
main:                                   # @main
        push    rbp
        mov     rbp, rsp
        mov     dword ptr [rbp - 4], 0       # default return value

        mov     dword ptr [rbp - 8], 0       # int i=0;

        mov     eax, dword ptr [rbp - 8]
        add     eax, 1
        mov     dword ptr [rbp - 8], eax
        cmp     eax, 4                       # uses the i++ result still in a register
        jne     .LBB0_2                      # jump over if() body
        jmp     .LBB0_2                      # jump over else body, I think.
.LBB0_2:

        mov     eax, dword ptr [rbp - 8]
        mov     ecx, eax
        add     ecx, 1                       # i++ uses a 2nd register
        mov     dword ptr [rbp - 8], ecx
        cmp     eax, 4
        jne     .LBB0_4
        jmp     .LBB0_4
.LBB0_4:

        xor     eax, eax                     # return 0

        pop     rbp                          # tear down stack frame.
        ret