WIN32汇编语言教程:第03章 使用MASM · 3.6 代码风格(1)

随着程序功能的增加和版本的提高,程序越来越复杂,源文件也越来越多,风格规范的源程序会对软件的升级、修改和维护带来极大的方便,要想开发一个成熟的软件产品,必须在编写源程序的时候就有条不紊,细致严谨。

在编程中,在程序排版、注释、命名和可读性等问题上都有一定的规范,虽然编写可读性良好的代码并不是必然的要求(世界上还有难懂代码比赛,看谁的代码最不好读懂!),但好的代码风格实际上是为自己将来维护和使用这些代码节省时间。本节就是对汇编语言代码风格的建议。

3.6.1 变量和函数的命名

1. 匈牙利表示法

匈牙利表示法主要用在变量和子程序的命名,这是现在大部分程序员都在使用的命名约定。“匈牙利表示法”这个奇怪的名字是为了纪念匈牙利籍的Microsoft程序员Charles Simonyi,他首先使用了这种命名方法。

匈牙利表示法用连在一起的几个部分来命名一个变量,格式是类型前缀加上变量说明,类型用小写字母表示,如用h表示句柄,用dw表示double word,用sz表示以0结尾的字符串等,说明则用首字母大写的几个英文单词组成,如TimeCounter,NextPoint等,可以令人一眼看出变量的含义来,在汇编语言中常用的类型前缀有:

b         表示byte
w         表示word
dw        表示dword
h         表示句柄
lp        表示指针
sz        表示以0结尾的字符串
lpsz          表示指向0结尾的字符串的指针
f         表示浮点数
st        表示一个数据结构

这样一来,变量的意思就很好理解:

hWinMain          主窗口的句柄
dwTimeCount    时间计数器,以双字定义
szWelcome      欢迎信息字符串,以0结尾
lpBuffer          指向缓冲区的指针
stWndClass     WNDCLASS结构
...

很明显,这些变量名比count1,abc,commandlinebuffer和FILEFLAG之类的命名要易于理解。由于匈牙利表示法既描述了变量的类型,又描述了变量的作用,所以能帮助程序员及早发现变量的使用错误,如把一个数值当指针来使用引发的内存页错误等。

对于函数名,由于不会返回多种类型的数值,所以命名时一般不再用类型开头,但名称还是用表示用途的单词组成,每个单词的首字母大写。Windows API是这种命名方式的绝好例子,当人们看到ShowWindow,GetWindowText,DeleteFile和GetCommandLine之类的API函数名称时,恐怕不用查手册,就能知道它们是做什么用的。比起int 21h/09h和int 13h/02h之类的中断调用,好处是不必多讲的。

2. 对匈牙利表示法的补充

使用匈牙利表示法已经基本上解决了命名的可读性问题,但相对于其他高级语言,汇编语言有语法上的特殊性,考虑下面这些汇编语言特有的问题:

● 对局部变量的地址引用要用lea指令或用addr伪操作,全局变量要用offset;对局部变量的使用要特别注意初始化问题。如何在定义中区分全局变量、局部变量和参数?

● 汇编的源代码占用的行数比较多,代码行数很容易膨胀,程序规模大了如何分清一个函数是系统的API还是本程序内部的子程序?

实际上上面的这些问题都可以归纳为区分作用域的问题。为了分清变量的作用域,命名中对全局变量、局部变量和参数应该有所区别,所以我们需要对匈牙利表示法做一些补充,以适应Win32汇编的特殊情况,下面的补充方法是笔者提出的,读者可以参考使用:

● 全局变量的定义使用标准的匈牙利表示法,在参数的前面加下划线,在局部变量的前面加@符号,这样引用的时候就能随时注意到变量的作用域。

● 在内部子程序的名称前面加下划线,以便和系统API区别。

如下面是一个求复数模的子程序,子程序名前面加下划线表示这是本程序内部模块,两个参数——复数的实部和虚部用_dwX和_dwY表示,中间用到的局部变量@dwResult则用@号开头:

_Calc      proc    _dwX,_dwY
           local   @dwResult
 
           finit
           fild    _dwX
           fld     st(0)
           fmul       ;i * i
           fild    _dwY
           fld     st(0)
           fmul       ;j * j
           fadd       ;i * i + j * j
           fsqrt      ;sqrt(i * i + j * j)
           fistp   @dwResult ;put result
           mov     eax,@dwResult
           ret
 
_Calc                endp       

本书中所有的示范源代码采用的都是这样的命名约定。

3.6.2 代码的书写格式

1. 排版方式

程序的排版风格应该遵循以下规则。

首先是大小写的问题,汇编程序中对于指令和寄存器的书写是不分大小写的,但小写代码比大写代码便于阅读,所以程序中的指令和寄存器等要采用小写字母,而用equ伪操作符定义的常量则使用大写,变量和标号使用匈牙利表示法,大小写混合。

其次是使用Tab的问题。汇编源程序中Tab的宽度一般设置为8个字符。在语法上,指令和操作数之间至少有一个空格就可以了,但指令的助记符长度是不等长的,用Tab隔开指令和操作数可以使格式对齐,便于阅读。如:

   xor eax,eax
   fistp dwNumber
   xchg eax,ebx

上述代码的写法就不如下面的写法整齐:

   xor        eax,eax
   fistp      dwNumber
   xchg   eax,       ebx

还有就是缩进格式的问题。程序中的各部分采用不同的缩进,一般变量和标号的定义不缩进,指令用两个Tab缩进,遇到分支或循环伪指令再缩进一格,如:

              .data
dwFlag    dd ?
             .code
start:
              mov    eax,dwFlag
             .if    dwFlag == 1
                     call       _Function1
             .else
                     call       _Function2
             .endif
           ...

合适的缩进格式可以明显地表现出程序的流程结构,也很容易发现嵌套错误,当缩进过多的时候,可以意识到嵌套过深,该改进程序结构了。

上页:第03章 使用MASM · 3.5 高级语法(2) 下页:第03章 使用MASM · 3.6 代码风格(2)

第03章 使用MASM

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号