那么有了上述三方面的基础,我们就可以来逐一解读那段“传奇”的汇编代码了。
初始化i
00411A3E mov dword ptr [i],0
跳转至条件判断
00411A45 jmp myfunction+30h (411A50h)
循环表达式,对i每轮加1
00411A47 mov eax,dword ptr [i]
00411A4A add eax,1
00411A4D mov dword ptr [i],eax
条件判断,若不满足i < 3,则跳出循环
00411A50 cmp dword ptr [i],3
00411A54 jge myfunction+0AEh (411ACEh)
{ i的循环体
这里比较特殊,传说中的“嵌套循环”,可以对比
最前面的C代码看一下,其实是一样的
初始化j
00411A56 mov dword ptr [j],0
跳至j循环的条件判断
00411A5D jmp myfunction+48h (411A68h)
每轮对j加1
00411A5F mov eax,dword ptr [j]
00411A62 add eax,1
00411A65 mov dword ptr [j],eax
j循环的条件判断
00411A68 cmp dword ptr [j],3
00411A6C jge myfunction+0A9h (411AC9h)
{ j的循环体
… 这里省略的是数组赋值的过程代码,会在稍后分析
} j 的循环体结束
跳到j的expr
00411AC7 jmp myfunction+3Fh (411A5Fh)
} i的循环体结束
跳至i的expr
00411AC9 jmp myfunction+27h (411A47h)
以下我们再详细分析下上面挖下来的数组赋值的代码。为了方便起见,再把C语句贴如下:
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
; 还记得前面所说的汇编访问二维数组的方式么?忘记的话,再回过头去看一下哈
; 第i行,每行3个占4字节的int类型,所以乘以i×12(0Ch),表示行偏移
00411A6E mov eax,dword ptr [i]
00411A71 imul eax,eax,0Ch
; 往寄存器送入一些变量
00411A74 mov ecx,dword ptr [a]
00411A77 mov edx,dword ptr [j]
00411A7A mov esi,dword ptr [b]
00411A7D mov eax,dword ptr [ecx+eax] ; ecx已经含有a数组的起始地址,这句相当于往
; eax存入基址+i行偏移这个地址开始的int类
; 型值,也就是a[i][0]
00411A80 imul eax,dword ptr [esi+edx*4] ; 这句比较好理解,esi是b数组起始地址
; edx * 4表示列偏移,相当于eax * b[0][j],
; 也就是eax = a[i][0] * b[0][j]
; 同理,ecx获得i行偏移量
00411A84 mov ecx,dword ptr [i]
00411A87 imul ecx,ecx,0Ch
00411A8A mov edx,dword ptr [a] ; edx保存的a数组基地址
00411A8D mov esi,dword ptr [j] ; esi = j
00411A90 mov edi,dword ptr [b] ; edi保存的b数组基地址
00411A93 mov ecx,dword ptr [edx+ecx+4] ; edx 基址 = a
; ecx 行偏移 = 第i行
; 4 列偏移 = 第1列
; 综上,ecx = a[i][1]
; edi b的基址;esi = j,第j列;0Ch,行偏移量,相当于是第1行
; 综上,ecx = ecx * b[1][j],即ecx = a[i][1] * b[1][j]
00411A97 imul ecx,dword ptr [edi+esi*4+0Ch]
; 把第一项a[i][0] * b[0][j]和第二项相加存入eax,从这也可以看出
; 其实eax就是用来保存最后结果的,此时eax = a[i][0] * b[0][j] + a[i][1] * b[1][j]
00411A9C add eax,ecx
; 以下这段不用解释了吧?依样花葫芦,到411AB6为止
; eax = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j]
00411A9E mov edx,dword ptr [i]
00411AA1 imul edx,edx,0Ch
00411AA4 mov ecx,dword ptr [a]
00411AA7 mov esi,dword ptr [j]
00411AAA mov edi,dword ptr [b]
00411AAD mov edx,dword ptr [ecx+edx+8]
00411AB1 imul edx,dword ptr [edi+esi*4+18h]
00411AB6 add eax,edx
; 首先是老规矩,通过ecx与edx找到了c[i][j]的内存地址,411AC4这句就是把
; 前边eax中的计算结果写入到内存中代表c[i][j]这个元素的位置
00411AB8 mov ecx,dword ptr [i]
00411ABB imul ecx,ecx,0Ch
00411ABE add ecx,dword ptr [c]
00411AC1 mov edx,dword ptr [j]
00411AC4 mov dword ptr [ecx+edx*4],eax
这样就完成了a中第i行每个元素分别与b中第j列的每个元素的乘积保存到c的第i行第j列元素中,这么一个操作,确实比较复杂。如果看不大懂,不要紧,回过头多看几遍,一定会明白的,如果说有些话我的表述不妥当,也请大侠指出,以免误人子弟J
我的一些朋友问我,研究汇编,尤其研究C反汇编,到底有什么用处?我的理解是,除非你是底层开发人员,否则我们的目的并非是学习如何运用汇编写复杂的算法程序,而是将它应用在排错、性能优化等方面,如果你能看懂一些汇编代码,那么当你的客户程序崩溃时,你打开调试器,就可以先简要分析出出错程序代码的大致意思,这对你的调试是相当有帮助的。而如果汇编懂得较深的话(我只是皮毛而已),那么就可以对C目标程序进行有目的的修改以提高程序性能,因为有时候即便是release模式下的exe文件,仍然有可以优化之处,只是这时一定要谨慎再谨慎,万一捡了芝麻丢了西瓜就太不划算了。当然了还有另外某些用途,呵呵,比较邪恶,就不点破了……总之希望本文能对大家有所帮助!