返回介绍

25.1 整数值

发布于 2025-02-22 14:00:49 字数 2722 浏览 0 评论 0 收藏 0

#!cpp
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int celsius, fahr;
    printf ("Enter temperature in Fahrenheit:\n");
    if (scanf ("%d", &fahr)!=1)
    {
        printf ("Error while parsing your input\n");
        exit(0);
    };
    celsius = 5 * (fahr-32) / 9;
    if (celsius<-273)
    {
        printf ("Error: incorrect temperature!\n");
        exit(0);
    };
    printf ("Celsius: %d\n", celsius);
};

25.1.1 MSVC 2012 x86 /Ox

清单 25.1: MSVC 2012 x86 /Ox

#!bash
$SG4228 DB ’Enter temperature in Fahrenheit:’, 0aH, 00H
$SG4230 DB ’%d’, 00H
$SG4231 DB ’Error while parsing your input’, 0aH, 00H
$SG4233 DB ’Error: incorrect temperature!’, 0aH, 00H
$SG4234 DB ’Celsius: %d’, 0aH, 00H
_fahr$ = -4 ; size = 4
_main PROC
    push ecx
    push esi
    mov esi, DWORD PTR __imp__printf
    push OFFSET $SG4228 ; ’Enter temperature in Fahrenheit:’
    call esi ; call printf()
    lea eax, DWORD PTR _fahr$[esp+12]
    push eax
    push OFFSET $SG4230 ; ’%d’
    call DWORD PTR __imp__scanf
    add esp, 12 ; 0000000cH
    cmp eax, 1
    je SHORT $LN2@main
    push OFFSET $SG4231 ; ’Error while parsing your input’
    call esi ; call printf()
    add esp, 4
    push 0
    call DWORD PTR __imp__exit
    $LN9@main:
    $LN2@main:
    mov eax, DWORD PTR _fahr$[esp+8]
    add eax, -32 ; ffffffe0H
    lea ecx, DWORD PTR [eax+eax*4]
    mov eax, 954437177 ; 38e38e39H
    imul ecx
    sar edx, 1
    mov eax, edx
    shr eax, 31 ; 0000001fH
    add eax, edx
    cmp eax, -273 ; fffffeefH
    jge SHORT $LN1@main
    push OFFSET $SG4233 ; ’Error: incorrect temperature!’
    call esi ; call printf()
    add esp, 4
    push 0
    call DWORD PTR __imp__exit
    $LN10@main:
    $LN1@main:
    push eax
    push OFFSET $SG4234 ; ’Celsius: %d’
    call esi ; call printf()
    add esp, 8
    ; return 0 - at least by C99 standard
    xor eax, eax
    pop esi
    pop ecx
    ret 0
$LN8@main:
_main ENDP

关于这个我们可以说的是:

?printf() 的地址先被载入了 ESI 寄存器中,所以 printf() 调用的序列会被 CALL ESI 处理,这是一个非常著名的编译器技术,当代码中存在多个序列调用同一个函数的时候,并且/或者有空闲的寄存器可以用上的时候,编译器就会这么做。 
?我们知道 ADD EAX,-32 指令会把 EAX 中的数据减去 32。 EAX = EAX + (-32) 等同于 EAX = EAX - 32,因此编译器决定用 ADD 而不是用 SUB,也许这样性能比较高吧。 
?LEA 指令在值应当乘以 5 的时候用到了: lea ecx, DWORD PTR [eax+eax*4]。 是的,i + i * 4 是等同于 i*5 的,而且 LEA 比 IMUL 运行的要快。 还有,SHL EAX,2/ ADD EAX,EAX 指令对也可以替换这句,而且有些编译器就是会这么优化。 
?用乘法做除法的技巧也会在这儿用上。 
?虽然我们没有指定,但是 main() 函数依然会返回 0。C99 规范告诉我们[15 章, 5.1.2.2.3] main() 将在没有 return 时也会照常返回 0。 这个规则仅仅对 main() 函数有效。 虽然 MSVC 并不支持 C99,但是这么看说不好他还是做到了一部分呢?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文