- 第一章 CPU 简介
- 第二章 Hello,world!
- 第三章 函数开始和结束
- 第四章 栈
- Chapter 5 printf() 与参数处理
- Chapter 6 scanf()
- CHAPER7 访问传递参数
- Chapter 8 一个或者多个字的返回值
- Chapter 9 指针
- Chapter 10 条件跳转
- 第 11 章 选择结构 switch()/case/default
- 第 12 章 循环结构
- 第 13 章 strlen()
- Chapter 14 Division by 9
- chapter 15 用 FPU 工作
- Chapter 16 数组
- Chapter 17 位域
- 第 18 章 结构体
- 19 章 联合体
- 第二十章 函数指针
- 第 21 章 在 32 位环境中的 64 位值
- 第二十二章 SIMD
- 23 章 64 位化
- 24 章 使用 x64 下的 SIMD 来处理浮点数
- 25 章 温度转换
- 26 章 C99 的限制
- 27 章 内联函数
- 第 28 章 得到不正确反汇编结果
- 第 29 章 花指令
- 第 30 章 16 位 Windows
- 第 31 章 类
- 三十二 ostream
- 34.2.2 MSVC
- 34.2.3 C++ 11 std::forward_list
- 34.3 std::vector
- 34.4 std::map and std::set
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
25.1 整数值
#!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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论