瑞星卡卡安全论坛技术交流区系统软件 【转载】软件开发人员必备工具书 【代码大全】

«910111213141516   13  /  16  页   跳转

【转载】软件开发人员必备工具书 【代码大全】

为在书中专门用一节来论述这个问题是小题大做。但不管怎样,你毕竟在建立变量上花费了许
多时间,因此,养成这方面的良好习惯以避免不必要的失败和徒劳的努力是非常必要的。
8.4.1  使用模框( template e)进行变量说明)
在一个文件中存储一个变量说明模框。需要说明新的变量时,你可以把这个模框调入程序,
对其进行编辑以说明新变量。下面是用C 写成的一个模框:
extern * *; /* */
static * *; /* */
* *; /* */
这个模框有几个优点。首先,很容易从中选择出与你要求最接近的行,然后删掉其余各行;
第二,每行中“*”的作用是占有位置,这使得进入每行的编辑位置都非常容易;第三,如果你
忘记了更改“*”,那么一定会产生语法错误,从而起到了提醒的作用;第四,使用模框可以保
持说明形式的一致性;最后,预留的注释空格将提醒你在说明变量时进行注释,这简化了以后
的程序注释工作。
不过,不要以为你必须采用与上例完全相同的模框,尽管按自己的想法去建立自己的模框,
只要它能降低变量说明工作量、增强代码可读性并且使调试更容易就可以了。我的一个朋发给
自己找出了一个注释变量的理由。她名叫Chris,她的模框是这样的:

*  *  { Chris is a jerk! }

8.4.2  隐式说明
有些语言具有隐含变量说明功能。例如,如果在Basic或Fortran 中你没有说明就使用了某
些变量,那么编译程序将自动说明。
隐式说明是所有语言特性中的最具危害性的特性之一。
如果你用Fortran 或 Basic编过程序,那你一定知道要找出 ACCTNO的值不正确是多么的
困难。而你最后却发现这是由于ACCTNUM被重新初始化为零的原因,如果你所用的语言不要
求对变量做出说明,那么,这种错误是很常见的。
而如果你所用的程序语言要求对变量作出说明,那么在出现上例中的错误之前,你必须首
先先犯两个另外的错误才行。首先,你要错误地同时把ACCTNUM和ACCTNO 放入了子程序
中;其次,你要错误地在子程序的说明段中同时说明了这两个变量。第二个错误是很难出现的,
因此,要再犯上例中的错误几乎是不可能的。要求你对变量进行显式说明的语言其主要优点之
一,便是可以使你在使用变量时更加谨慎。但如果你用的是具备隐式说明功能的语言,那你该
怎么办呢?以下给出了一些指导原则:
关闭隐式说明功能。有些编译程序允许关闭这一功能。比如,在Fortran 中,可以使用一个
叫IMPLICIT NONE的语句,这并不是 ANSI FORTRAN77 中的标准语句,但许多Fortran 中都
有这一扩展。
说明所有变量。即使编译程序不要求,但你每次使用新变量时,都要坚持对它做出说明。
这样并不能避免所有的错误,但是至少可以避免某些错误。
使用命名约定。建立一个关于常用后缀(如NUM和NO)的命名约定,这样,当你想要
gototop
 

用一个变量时就不会误了两个。
检查变量名。利用你的编译程序或其它工具软件产生的参考表。许多编译程序都会列出你
在子程序中使用的所有变量,从而使你发现ACCTNUM和ACCTNO中的错误。同时,它也会
指出说明但并未使用的变量。
8.5  初始化数据的准则
在编程中,最大的错误原因之一便是对数据不恰当的初始化。开发有效地避免初始化错误
的技术,可以节约大量的调试时间。
不恰当初始化产生的问题,是由某一变量带有你没有预料的初值引起的。这可能是由下述
原因中的任何一种引起的:
· 这个变量未被赋过任何值。其值是在程序开始运行时,由在它的内存中的位置偶然的
值决定的。
· 变量的值已经过时了。变量在某点中被赋过值,但是这个值已不再有效了。
· 变量的一部分被赋了值,而另一个部分没有被赋值。如果你使用的是指针变量,那么
常见的错误是用指针分配了内存,却忘记了对指针所指的变量进行初始化。这与对变
量根本不赋值的效果是一样的。
这种情况往往有几种表现形式。你可能初始化了结构的某一部分而没有初始化另一部分;
也可能会分配内存,然后初始化变量,但指向变量的指针却没有被初始化,这意味着你是随意
地拿一段内存并赋予它们一些值,这段内存可能是存储数据的,也可能是存储代码的,也可能
是被操作系统所占用的。这种错误的表现形式是多种多样的,而且它们之间往往每次都有着天
壤之别。这使得调试指针错误要比调试其它错误困难得多。
下面是如何避免初始化错误的一些准则:
检查输入参数的有效性。初始化的另一种有价值的形式是检查输入参数的有效性。在赋给
输入参数任何值之前,要首先确认它是合理的。
在使用变量的位置附近对其进行初始化。有些程序员喜欢在子程序开始的一个地方对所有
变量进行初始化,以下是用Basic语言写成的例子:
' initialize all variables
Idx = 0
Total = 0
Done = False
...
'lots of code using Idx and Total
...
'code using Done
while not Done
...
gototop
 

另外一些程序员则尽可能在每一次用到变量的位置附近对其进行初始化,下面也是用Basic
写成的例子:

Idx = 0
'code using Idx
...
Total = 0                ── Total 在使用位置附近初始化
'code using Total
...
Done = False  ── Done在使用位置附近初始化
'code using Done
while not Done
...

第二个例子要好于第一个,其中有几个原因。在第一个例子中,当运行到使用Done 的代
码时,Done 很有可能已经被改变过了。即使在初次写程序时不会出现这种情况的话,那么以后
你对其进行修改时,很可能会出现这种情况。第一种方法的另一个问题是,把所有变量集中进
行初始化,会给人以这些参数将在程序中随处都被用到的印象,而事实上Done 只在程序结束
前才被用到;最后,当对程序进行改动时(这是很可能的,起码在调试时要修改),可能会把
Done包含在循环中,从而需要对Done重新进行初始化,在这种情况下,第二个例子中的代码
不会有什么太大的变化。而第一个例子中的代码则可能会产生讨厌的初始化错误。
这也是邻近原则的一个例子:把相关的操作放在一起。这一原则也适用于把对代码的注释
放在相应的代码附近,把循环设定代码放在循环附近,把注释放在非循环代码中。
要特别注意计数器和累加器。变量i、j、k和Sum、Total等往往代表计数器和累加器。常
见的错误是在下次用到它们时,忘记了对其进行清零操作。
查找需要重新进行初始化的地方。首先问自己一下有哪些地方需要重新进行初始化。重新
初始化的原因可能是由于变量在循环中被用过多次,也可能是由于变量在对子程序的调用中间
要保持原值且需清零。如果需要重新初始化的话,要确保初始化语句是在被重复的代码段中。
对命名常量只初始化一次,用可执行代码初始化变量。如果要用变量来模仿命名常量,那
么在程序的开始对它们进行一次初始化是可以的,在Fortran 中可以用Data 语句来做到这点。
在其它语言中,可以在Startup()子程序中初始化它们。
应在使用变量的位置附近的可执行代码对其进行初始化。最常见的改动之一是改动某一子
程序,变一次调用它为多次调用它。在DATA 语句或Startup()子程序中被初始化的变量,在程
序中不能被重新初始化。
按照所说明的对每个变量进行初始化。虽然其地位无法与在变量使用位置附近对其初始化
相比,但是按照所说明的初始化变量,仍然是防错性编程中的一件有力工具。如果你养成习惯,
那就可以有效地防止初始化错误。下例就保证了student_name 在每次调用含有它的子程序时,
都将被重新初始化。
gototop
 

char student_name[NAME_LENGTH+1] = {'\0'}; /* full name of student */

利用编译程序的警告信息。许多编译程序都会对使用未初始化的变量进行警告。
设置编译程序使其自动初始化所有变量。如果你的编译程序支持这种选择项,那么让它对
所有变量进行初始化是非常简单的。但是,由于对编译程序的依赖性。当把程序移到另一台编
译程序不同的机器上时,则有可能产生问题。这时要确保你对所用的编译程序进行了注释,否
则是很难发现程序对编译程序有依赖性。
使用内存存取检查程序来查找无效的指针。在某些操作系统中,操作系统代码会自动查找
无效指针,在其它情况下,就只有依靠自己了。不过,你也不一定非得靠自己。可以买一个内
存存取检查程序来检查程序中的指针操作。
在程序开始初始化工作内存。把工作内存初始化到某一个值可以帮助发现初始化错误。可
以采取以下任何一种方法:
· 可以用程序内存填充程序来赋予内存某一已知值,这个值用0比较好,因为它保证未
初始化指针指向低内存,比较容易发现它们,在80X86 处理器中,16 进制0CCH 比
较好,因为它是机器的断点中断码。如果是在调试中运行数据而不是代码,你就会进
入断点。使用值0CCH 的另一个优点是在内存转储中它很容易辨认。
· 如果你使用的是内存填充程序,可以每次改变一个填充值,用这种方法来检查一下运
行环境下隐藏的错误。
· 可以用程序在软件运行时初始化工作内存。虽然使用内存填充程序的目的是把错误暴
露出来,但这种技术的目的则是隐藏它们。通过每次把工作内存由相同的值充满,可
以保证在每次运行时程序不会被启动时的随机因素干扰。
8.5.l  检查表
数据生成
生成类型
· 程序中是否对每种可能变动的数据都使用了不同的类型?
· 类型名称是面向客观世界中的实体类型而不是面向程序语言中的类型吗?
· 类型名称是否有足够的表现力来帮助说明数据?
· 避免重新定义已定义的类型了吗?
说明数据
· 是否使用了模框为简化数据说明工作?并用其来统一说明形式?
· 如果所用的语言支持隐式说明,是否采取了补救措施?
初始化
· 是否每个子程序都对输入参数进行了检查以保证其有效性?
· 是否在使用变量的位置附近对其进行初始化的?
· 是否恰当地对计数器和指针进行了初始化?是否在必要时对其进行了重新初始化?
· 在反复执行的代码段中,是否对变量进行了恰当地重新初始化?
· 用编译程序编译代码时是否是无警告的?
gototop
 

8.6  小结
· 在你的工具箱中需要一张全部数据结构的清单,以便用最合适的方法处理每一种问题。
· 建立自己的数据类型,以增加程序的可变动性,并使其成为自说明的。
· 数据初始化很容易产生错误,因此应采用本章推荐的技术来避免由意外初始值所产生的错
误。
gototop
 

第九章  数据名称

目录
    9.1  选择名称
9.2  特定数据类型命名
9.3  命名约定
9.4  非正式命名约定
9.5  匈牙利命名约定
9.6  短名称
9.7  要避免的名称
9.8  小结
相关章节
生成数据:见第8 章
使用变量时要考虑的问题:见第10 章
说明数据:见19.5 节
格式化数据说明:见 18.5 节
   
    本章论述的是如何对数据进行恰当命名。这个主题对有效编程是非常重要的,但是在进行
恰当命名时要考虑的几十个问题中,我却只读过对其中两到三个的讨论。大多数编程课本只用
几段来论述名称缩写的选择问题,讲的也都是些关于这方面的陈词滥调,而完全寄希望于你自
己去解决问题。对于另一个极端,使用过多关于命名的信息将你淹没,我认为简直就是犯罪。
9.1  选择名称
    在给变量命名时,你不能像给小狗起名时那样仅仅挑有趣或好听的名字。但除了实体不同
之外,变量与变量名和狗与狗名实际是同一回事。
    因此,一个变量的好坏在很大程度上是由其名字决定的。所以在选择变量名时一定要谨慎。
    以下是一段使用不恰当变量名的例子(用C 写成);
X = X - XX;
XXX = Aretha + SalesTax( Aretha );
X = X + LateFee(X1,X)+ XXX;
X = X + Interest(X1,X);
    这段代码是干什么的?X1,XX 和XXX 代表的是什么呢?Aretha 的意思是什么?如果某
人告诉你,这段程序是根据收支平衡和新的购买情况来计算顾客和全部账单的,那么你将使用
什么变量来代表那些新购买活动呢?
gototop
 

以下是一个同样内容的新程序,这里,上述问题就很容易回答了。
Balance = Balance - LastPayment;
MonthlyTotal = NewPurchases + SalesTax( NewPurchases );
Balance = Balance + LateFee( CustomerID, Balance ) + MonthlyTotal;
Balance = Balance + Interest( CustomerID,Balance );
    通过比较两段代码,我们发现好的变量名是易读、易记而且是恰当的。可以使用几条通用
原则来达到这些目的。
9.1.1  命名时要考虑的最重要问题
    在给变量命名时,考虑的问题是变量名称是否完全而又准确地描述了变量所代表的实体。
一个有效的方法是用自然语言(如英语)将变量所代表的实体描述出来。往往这一描述本身便
是最好的名称,因其不含缩写它很容易读懂,又由于它是对实体的全面描述。因此不会与其它
实体相混淆,而且因为它与概念相近,所以也很容易记。
比 如 要 用 一 个 变 量 来 代 表 美 国 奥 林 匹 克 队 的人数,你可以对其命名为
NumberofPeople0nTheUSOlympicTeam。对代表自1896 年以来国家队在奥林匹克运动会上最多
得分的变量可以用MaximumNumberofPointsSince1986作为其名称。而用InterestRate 或Rate作
为代表目前利率的变量名要比用r或x 好得多。
    你应该可以发现这些名字的两个特点。首先,它们很容易解释。事实上,你根本不需要解
释,它们的意思是一目了然的;第二条则是其中有些名字很长,长得根本不实用。稍后我们将
讨论这一问题。
    下面是一些变量名的例子,同时列出了好的和坏的。
变量代表的实体  恰当的名称  不恰当的名称
火车速度  Velocity、TrainVelocity、VolocityInMPH VELT,V,TV,X,X1
今天日期  CurrentDate、CrntDate CD,Current,C,X
每页行数  LinesPerPage LPP , Lines , X , X1
    CurrentDate 和CrntDate是恰当的名字,因为它们全面准确地描述了“今天日期”这一含义。
而且,它们用的是明显的单词。程序员们往往忽视使用平常的词,而事实上这是最简单的解决
办法;CD 和C 太简短了,说明不了任何问题;Current 并没有说明现在的什么?是总统还是赌
马的结果?Date像是一个不错的名字,但究竟是什么时候的Date?是基督出生那天吗?X,X1
在任何情况下几乎都是不好的名字,因为它们通常都是代表未知量的,如果你要它代表其它实
体时,往往会引起误会。
9.1.2  面向问题
    一个好记的名字通常是面向问题而不是解决问题的。一个恰当的名字往往说明是“什么”
而不是“怎样”。通常,如果一个名称指向计算的一方面而不是指向问题,那么可以认为之是个
“怎样”而不是“什么”的名称。要避免使用这种名称而要使用面向问题的名称。
    雇员数据的记录可称为InputRec或EmPloyeeData,InputRec 是一个计算机术语,指的是输
入和记录;EmPloyeeData指的是问题域而不是计算方面。同样,对一个表示打印机状态的变量
gototop
 

来说,BitFlag 要比PrinterReady专业化得多。在计帐系统中,CalcVal要比Sum更加专业化。
9.1.3  最佳名称长度
    名称的最佳长度应介于MaximumNumberOfPointsSincel896 和x 之间。太短 的名字 往 往
表示不出完整的意思,而用x1和x2来表示的问题,即使你能找出x1 代表的是什么,也很难发
现x1 和x2 之间有什么联系。太长的名字难以输入,而且会对软件的可视结构产生破坏作用。
    Gorla 和Benander 在1990 年发现当变量名长度在10 到16 个字母时,COBOL程序最容易
调试。而变量名称长度在8 到20 个字母时,程序调试容易几乎是一样的。这一准则并不是说你
必须把所有变量名长度都限制在9到15或10到16个字母之间。但这确实要求你检查一下,程
序中变量名长于这个标准的那些变量,确保清楚程度符合需求。
    以下是对一些变量名的评价,或许会给你一些启迪:
    名称太长的:
NumberofPeopleOnTheUS0lympicTeam
NumberOfSeatsInTheSaddleDome
MaximumNumberOfPointsSince1896
名称太短的:
N,NP,NTM
N,NS,NSISD
M,MP,Max,Points
合适的:
NumTeamNumbers,TeamMbrCount,cTeamMbrs
NumSeatslnDome,SeatCount,cSeat
MaxTeamPoints,Record Points,cPoints
9.1.4  变量名的作用域
    短的变量名总是不好的吗?当然不总是。当你用i 作为一个变量名时,这个较短的长度就
可以说明某些问题,比如,这个变量是一个临时变量,只在有限的操作范围内才是有效的。
    当程序员读到这样一个变量时,他应该能猜到这个变量只在几行内使用。如果你把某个变
量称为“i”,你就等于在说“这个变量仅作为循环计数器或数组索引数使用,在这几行代码外
没有任何意义”。
    由W.J Hansen 进行的一项调查表明,较长的名字适于较常使用的变量或全局变量。而较
短的名字则适于局部变量或循环变量(1980)。但短名字会产生许多问题,有些程序员把避免使
用它们作为防错性编程的准则。
9.1.5  变量名中的计算值限定词
    许多程序中含有带有计算值的变量:totals,averages,maximums等等。如果你用限定词诸
如(Ttl,Sum,Avg 等)来改动变量,那要把它们放在最后。
    这种方法有几个优点。第一,变量名中最有意义的部分——表达变量名大部分意义的部分,
gototop
 

被放在最前面;第二,通过建立这种约定,在同一程序中同时使用TtlRevenue 和 RevenueTtl
会避免因此而引起的混淆;第三,像RevenueTtl,ExpenseTtl,RevenueAvg 和ExpenseAvg 这
样的一系列名字有一种令人愉快的相似感。而像TtlRevenue,ExpenseTtl,RevenueAvg 和
AvgExpense则不具备这种相似性。最后,一致性可以改善可读性并简化了维护。
    这个准则的例外是Num放在变量名前面时表示全部,如NumSales表示的全部商品的数额;
当Num 放在变量名后面时,它表示下标,SaleNum 表示现在标出的商品是第几个。Numsales
末尾的S也是表示两者意义区别的一个标志。但是,由于过分频繁地使用Num会产生混淆,所
以往往用Count来表示总数,而用Index 来表示下标。这样,SalesCount表示的是卖出的总数,
而Saleslndex 则指的是卖出的某一种特定商品。
9.1.6  变量名中的反义词
应恰当使用反义词。使用关于反义词的命名约定可以帮助保持连续性,同时也可以提高可
读性。像first/last这样一组反义词是很容易理解的,但first/end就有些让人摸不着头脑了,以下
是一些比较容易理解的反义词。
add/remove begin/end create/destroy
insert/delete first/last get/release
increment/decrement put/get up/down
lock/unlock min/max next/previous
old/new open/close show/hide
source/destination source/target start/stop
9.2  特定数据类型命名
    除了对数据命名通常需要考虑的一些问题之外,对特殊数据类型必须给予特殊的考虑。本
书将论述循环变量、状态变量、临时变量、逻辑变量、枚举变量和命名常量的命名问题。
9.2.l  循环变量命名
    由于几乎每个程序中都含有循环,因此,对循环变量的命名问题加以专门考虑是十分必要
的。
    简单循环中的循环控制变量的名字往往也是十分简单的,常用的是i、j、k 等。下面是一个
Pascal循环的例子:
for i:=FirstItem to LastItem do
Data := 0;
    如果这个变量还要在循环外使用,那么应该用比i、j、k 更能说明问题的名称。比如,你正
从文件中读取记录,并且要知道已经读取了多少个记录,那么用 RecordCount 作为其名称似乎
更合适些,请看下面的这个Pascal程序:
RecordCount:= 0
while not eof(InputFile) do
gototop
 

begin
RecordCount := RecordCount + 1;
ReadLn ( Score [ Recordcount ] )
end;
{ lines using RecoudCount }

    如果循环体长度较长的话,那就很容易使人忘记它代表的是什么,因此最好给循环控制变
量一个富有意义的名字。由于经常进行更改,扩展和拷贝等代码到另一个程序中,因此,大多
数有经验的程序员都避免用i、j、k 这类的名字。
    使循环体变长的一个常见原因是嵌套。因此,对于一个有多重嵌套的循环,最好给循环控
制变量以较长的名字以便改善其可读性:
for TeamIndex := i to TeamCount to begin
for EventIndex := 1 to EventCount[ TeamIndex ] do
Score[ TeamIndex, EventIndex ] := 0
end;
    通过精心对循环控制变量进行命名,可以避免它们的交叉:当你想用i 时误用了j,或者
想用j 时却又误用了i。这样做也可以使对数组的存取操作更为明了。Score [ TeamIndex,
EvenIndex ]显然要Score[i,j]更能说明问题。总之,应尽量避免使用 i、j、k 来命名。如果不得
不使用它们的话,那除了把它们用作循环控制变量之外,最好不再用作别的变量名。这一约定
是众所周知的,如果不遵守它只会引起别人的困惑。
9.2.2  状态变量命名
    状态变量描述的是程序所处的状态。下面论述了它们的命名原则。
    用比flag 更好的名称来命名变量,最好不用flag 作为状态变量的名字。之所以要避免使用
flag 作为标志名称,是因为它不能告诉你关于这个标志的任何信息。为了清楚起见,应该给标
志赋值,并且用枚举类型、命名常量或当作命名常量使用的全局变量对其进行测试。下面是一
个在C 语言中不恰当命名标志的例子。
if( Flag )...
if( StatusFlag & 0x0F )...
if( PrintFlag == 16 )...
if( ComputerFlag == 0 )...
 
Flag = 0x1;
StatusFlag = 0x80;
PrintFlag = 16;
ComputerFlag = 0;
    如果这个程序不是你写的,而且也没有注释告诉你的话,你是无法知道类似statusFlag =
0x80 之类的语句到底是要干什么的,而且你也无法知道Statusflag和0x80 到底是什么意思。以
gototop
 
«910111213141516   13  /  16  页   跳转
页面顶部
Powered by Discuz!NT