长久以来,C 和 C++ 编译器都会生成汇编语言源代码,但是程序员通常看不到。这是因为,汇编语言代码只是产生可执行文件过程的一个中间步骤。幸运的是,大多数编译器都可以应要求生成汇编语言源代码文件。 例如,下表列出了 Visual Studio 控制汇编源代码输出的命令行选项。

命令行

列表文件内容

/FA

仅汇编文件

/FAc

汇编文件与机器码

/FAs

汇编文件与源代码

/FAcs

汇编文件、机器码和源代码

检查编译器生成的代码文件有助于理解底层信息,比如堆栈帧结构、循环和逻辑编码,并且还有可能找到低级编程错误。另一个好处是更加便于发现不同编译器生成代码的差异。

现在来看看 C++ 编译器生成优化代码的一种方法。由于是第一个例子,因此先编写一个简单的 C 方法 Array Sum,并在 Visual Studio 2012 中进行编译,其设置如下:

Optimization=Disabled ( 使用调试器时需要 )

Favor Size or Speed=Favor fast code

Assembler Output=Assembly With Source Code

下面是用 ANSI C 编写的 arraysum 源代码:

int arraySum( int array[], int count )

{

int i;

int sum = 0;

for(i = 0; i < count; i++)

sum += array[i];

return sum;

}

现在来查看由编译器生成的 arraysum 的汇编代码,如下所示。

_sum$ = -8 ; size = 4

_i$ = -4 ; size = 4

_array$ = 8 ; size = 4

_count$ = 12 ; size = 4

_arraySum PROC ; COMDAT

;4 : {

push ebp

mov ebp, esp

sub esp, 72 ; 00000048H

push ebx

push esi

push edi

;5 : int i;

;6 : int sum = 0;

mov DWORD PTR _sum$[ebp], 0

;7 :

;8 : for(i = 0; i < count; i++)

mov DWORD PTR _i$[ebp], 0

jmp SHORT $LN3@arraySum

$LN2@arraySum:

mov eax, DWORD PTR _i$[ebp]

add eax, 1

mov DWORD PTR _i$[ebp], eax

$LN3@arraySum:

mov eax, DWORD PTR _i$[ebp]

cmp eax, DWORD PTR _count$[ebp]

jge SHORT $LN1@arraySum

;9 : sum += array[i];

mov eax, DWORD PTR _i$[ebp]

mov ecx, DWORD PTR _array$[ebp]

mov edx, DWORDPTR _sum$[ebp]

add edx, DWORD PTR [ecx+eax*4]

mov DWORD PTR _sum$[ebp], edx

jmp SHORT $LN2@arraySum

$LNl@arraySum:

;10 :

;11 : return sum;

mov eax, DWORD PTR _sum$[ebp]

;12 : }

pop edi

pop esi

pop ebx

mov esp, ebp

pop ebp

ret 0

_arraySum ENDP

1〜4 行定义了两个局部变量 (sum 和 i) 的负数偏移量,以及输入参数 array 和 count 的正数偏移量:

_sum$ = -8        ; size = 4

_i$ = -4              ; size = 4

_array$ = 8        ; size = 4

_count$ = 12     ; size = 4

9〜10 行设置 ESP 为帧指针:

push ebp

mov ebp,esp

之后,11〜14 行从 ESP 中减去 72,为局部变量预留堆栈空间。同时,把将会被函数修改的三个寄存器保存到堆栈。

sub esp, 72

push ebx

push esi

push edi

19 行把局部变量 sum 定位到堆栈帧,并将其初始化为 0。由于符号 _sum$ 定义为数值 -8,因此它就位于当前 EBP 下面 8 个字节的位置:

mov DWORD PTR _sum$[ebp],0

24 和 25 行将变量 i 初始化为 0,再转移到 30 行,跳过后面循环计数器递增的语句:

mov DWORD PTR _i$[ebp], 0

jmp SHORT $LN3@arraySum

26〜29 行标记循环开端以及循环计数器递增的位置。从 C 源代码来看,递增操作 (i++) 是在循环末尾执行,但是编译器却将这部分代码移到了循环顶部:

$LN2@arraySum:

mov eax, DWORD PTR _i$[ebp]

add eax, 1

mov DWORD PTR _i$[ebp], eax

30〜33 行比较变量 i 和 count,如果 i 大于或等于 count,则跳岀循环:

$LN3@arraySum:

mov eax, DWORD PTR _i$[ebp]

cmp eax, DWORD PTR _count$[ebp]

jge SHORT $LN1@arraySum

37〜41 行计算表达式 sum+=array[i]。Array[i] 复制到 ECX,sum 复制到 EDX,执行加法运算后,EDX 的内容再复制回 sum:

mov eax, DWORD PTR _i$[ebp]

mov ecx, DWORD PTR _array$[ebp]    ; array [i]

mov edx, DWORD PTR _sum$[ebp]      ; sum

add edx, DWORD PTR [ecx+eax*4]

mov DWORD PTR _sum$[ebp], edx

42 行将控制转回循环顶部:

jmp SHORT $LN2@arraySum

43 行的标号正好位于循环之外,该位置便于作为循环结束时进行跳转的目标地址:

$LN1@arraySum:

48 行将变量 sum 送入 EAX,准备返回主调程序。52〜56 行恢复之前被保存的寄存器,其中,ESP 必须指向主调程序在堆栈中的返回地址。

mov eax, DWORD PTR _sum$[ebp]

;    12 : }

pop edi

pop esi

pop ebx

mov esp, ebp

pop ebp

ret 0

_arraySum ENDP

可以写出比上例更快的代码,这种想法不无道理。上例中的代码是为了进行交互式调试,因此为了可读性而牺牲了速度。如果针对确定目标编译同样的程序,并选择完全优化,那么结果代码的执行速度将会非常快,但同时,程序对人类而言基本上是无法阅读和理解的。

调试器设置

用 Visual Studio 调试 C 和 C++ 程序时,若想查看汇编语言源代码,就在 Tools 菜单中选择 Options 以显示如下图的对话框窗口,再选择箭头所指的选项。上述设置要在启动调试器之前完成。接着,在调试会话开始后,右键点击源代码窗口,从弹出菜单中选择 Go to Disassembly。

本章目标是熟悉由 C 和 C++ 编译器产生的最直接和简单的代码生成例子。此外,认识到编译器有多种方法生成代码也是很重要的。比如,它们可以将代码优化为尽可能少的机器代码字节。或者,可以尝试生成尽可能快的代码,即使要用大量机器代码字节来输出结果 ( 常见的情况 )。

最后,编译器还可以在代码量和速度的优化间进行折中。为速度进行优化的代码可能包含更多指令,其原因是,为了追求更快的执行速度会展开循环。机器代码还可以拆分为两部分以便利用双核处理器,这些处理器能同时执行两条并行代码。

python编译成汇编语言_查看C语言/C++编译器生成的汇编语言代码相关推荐

  1. python编译成exe有意义吗_python工程编译成EXE

    前言 python编译成EXE文件后,可以独立使用.亲测,一个复杂的python工程包含多个包多个模块,可以生成exe文件. 目的 windows下将整个python工程编译成单个EXE或者单个目录带 ...

  2. python编译成exe和exe反编译成python

    先看文章概要.再看左侧目录,可准确找到需要内容 文章概要:python文件可编译成exe文件,exe文件也可通过反编译恢复为python文件.下面简单介绍如何生成exe和恢复python python ...

  3. 编译《自制编程语言 基于c语言》 郑钢 书中代码 idea

    编译<自制编程语言 基于c语言> 郑钢 书中代码 文章目录 编译<自制编程语言 基于c语言> 郑钢 书中代码 编译器 代码获取 正规途径 其他途径 运行 hello world ...

  4. python编译成汇编语言_Python前言之编程语言

    编程语言分类(语言) ​编程语言是用来和计算机进行交互的,计算机只认识0和1. 机器语言(低级语言) 直接和硬件进行交互 用0和1和计算机进行沟通 缺点:开发效率低 优点:执行效率高 汇编语言 直接和 ...

  5. Python编译成C语言,性能有多暴力?

    我这里用的Python环境是Anaconda3 2019.7 这里测试的程序是找出所有1000以内的勾股数. a∈[1, 1000],b∈[1, 1000], c∈[1, 1000] 满足a² + b ...

  6. python源码只有编译成二进制_【转】Python源代码编译成 pyc pyo

    原文地址 http://blog.csdn.net/sislcb/archive/2009/03/18/4002414.aspx什么是pyc文件 pyc是一种二进制文件,是由py文件经过编译后,生成的 ...

  7. python编译为机器码_通过 GraalVM 将 Java 程序编译成本地机器码!

    前言 2018年4月,Oracle Labs新公开了一项黑科技:Graal VM. 这是一个在HotSpot虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为"任何语言"的运行平台使 ...

  8. pyinstaller 把python编译成二进制

    我考,原来python也能编译成二进制,即脱离python环境运行,我还以为这只是c,c++,golang这些语言才能做的.好吧,我承认,我今天才知道.... pyinstaller 安装 1.    ...

  9. 看python源代码的顺序_查看“Python-2020-fall”的源代码

    因为以下原因,您没有权限编辑本页: 您所请求的操作仅限于该用户组的用户使用:用户 您可以查看与复制此页面的源代码.== Python程序设计课程主页(2020年秋季学期) == Teacher: [h ...

最新文章

  1. iserdese2接口详解_Xilinx Notes.docx
  2. 任务调度之Elastic-Job1
  3. python 字符串操作速度_强者一出,谁与争锋?与Python相比,C+的运行速度究竟有多快?|python|编程语言|字符串|示例|算法...
  4. 2017年 Java 程序员,风光背后的危机
  5. springmvc自定义404错误处理
  6. 如何使用网线连接mac登陆锐捷使用校园网——针对华南农业学子
  7. 如何长时间保存记忆,分享我的数据备份大法
  8. 中医针灸学综合练习题库【12】
  9. 基于Vector 的AUTOSAR NvM 模块使用
  10. SQL Server - 设置主键自增
  11. 苹果犯了“围师必阙”大忌
  12. 桌面计算机1008桌面计算机,windows桌面精灵
  13. python安装lap_AP 升级到 LAP,或 LAP 降到 AP 的方法
  14. GCC背后的故事OpenCV相逢何必曾相识
  15. 怎么把html文档转换成doc,Word文档如何把docx格式转换成doc格式
  16. 题目:输入某年某月某日,判断当日是星期几? 提示:1900年1月1日为星期一
  17. 朱亚杰 php,thinkphp框架怎么做一个三级菜单?
  18. 基于STM32的机器人控制仿真
  19. GSEA文件准备及表达相关性分析(R语言)
  20. 字符数组和字符指针做形参(字符型指针)

热门文章

  1. PHP连接pda,OTG接口是什么
  2. pythonrequests证书_requests的ssl证书验证、身份认证、cert文件证书
  3. python signature函数-tensorflow2.0的函数签名与图结构(推荐)
  4. python下载官网-Python2.7.10
  5. python对于设计师有什么用-Python前程无忧深圳UI设计师岗位分析
  6. python官网下载步骤-下载及安装Python详细步骤
  7. python在线学习直播-Python在线学习最有效马哥开启全网独家全程直播课
  8. python画柱状图-Python绘制精美图表之双柱形图
  9. python怎么把两个文件合并工具_python开发目录合并小工具 PathMerge
  10. PHP集锦点,php 函数集锦