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

«910111213141516   13  /  16  页   跳转

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

另外一些程序员则尽可能在每一次用到变量的位置附近对其进行初始化,下面也是用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
 

下是功能与内容相同但清楚得多的程序:
if ( DataReady )...
if ( CharacterType & PRINTABLE_CHAR )...
if ( ReportType == AnnualRpt )...
if ( RecalcNeeded == TRUE )...
DataReady = TRUE;
CharacterType = CONTROL_CHARACTER;
ReportType = AnnualRpt;
RecalcNeeded = FALSE;
    显然,第二个例子中CharacterType = CONTROL_CHARACTER 的意义要比第一个中
StatusFlag = 0x80 的意义要清楚得多。同样,第二个例子中的条件语句if ( ReportType ==
AnnualRpt ) 也显然要比第一个中的if ( PrintFlag == 16 )清楚得多。第二个例子表明,你可借
助预先命名的常量或枚举类型来使用这种方法。以下是如何利用枚举类型和命名常量来设置上
例中用到的值,仍用C 来实现:
/* values for DataReady and RecalcNeeded */
#define TRUE 1
#define FALSE 0
/* values for CharacterType */
#define LETTER 0x01
#define DIGIT 0x02
#define PUNCTUATION 0x04
#define LINE_DRAW 0x08
#define PRINTABLE_CHAR ( LETTER | DIGIT | PUNCTUATION | LINE_DRAW )

#define CONTROL_CHARACTER 0x80

/* values for ReportType */

Typedef enum { DailyRpt,MonthlyRpt,,QuarterlyRpt,
AnnualRpt,AllRpts}REPORT_TYPE_T;
    当你发现自己“侦破”了一段代码时,应该考虑对变量重新命名。侦破一桩凶杀案是可以
的,但你不应该去“侦破”一段代码。代码应该是具有良好可读性的。
9.2.3  临时变量命名
    临时变量用来保存中间运算结果,如用作暂时保留某个位置或保留内务操作值。通常把它
们叫做TEMP 或X 等没有什么意义的名字。临时变量的使用往往标志着程序员还没有完全理解
gototop
 

程序。而且,由于名义上给了它一个“临时”的状态,因而程序员们在处理它们时往往会采取
漫不经心的态度,从而增大了出错机会。
    要警惕“临时”变量。通常,暂时保留某些值是完全必要的。但如果在你的程序中出现很
多临时变量的话,则说明你对它们在程序中作用和使用它们的目的还不清楚。先让我们看一下
下面用C 语言写成一个例子:
/* Compute root of a quadratic equation.
This assumes that ( b^2 - 4 * a * c ) is positive. */

Temp = sqrt ( b^ 2 - 4 * a * c );
root[0] = ( -b + Temp )/( 2 * a );
root[1] = ( -b – Temp )/( 2 * a );
    把由公式sqrt ( b^2 - 4 * a * c ) 计算出来的值储存起来是个不错的想法,尤其是在后面还有
两处用到了它的情况下。但是用TEMP作为它的名称不能告诉你关于这个变量意义的任何信息。
一个较好的作法是下面这个例子:
/* Compute roots of a quadratic equation.
This assumes that ( b^2 – 4 * a * c )is positive */

Discriminant = sqrt ( b^2 - 4 * a *c );
root[0] = ( -b + Discriminant )/( 2 * a );
root[1] = ( -b - Discriminant )/( 2 * a );
    事实上两段代码是完全一样的,但是通过采用更准确也更能说明问题的变量名,大大改善
了其可读性。
9.2.4  逻辑变量命名
    以下命名逻辑变量的几条准则:
    记住一些典型的逻辑变量名。以下是些非常有用的逻辑变量名:
    Done。用Done 来表示某项工作已经完成了。这个变量可以表示子程序或循环是否已经完
成。当某项工作没有完成时,把Done 的值赋为False;当某项工作已经完成时,把Done 的值
赋为True。
    Error。用Error 来表示发生了错误。当没有错误时将Error 的值赋为False;当有错误时将

赋为True。
    Found。用Found 来表示是否找取了某个值。当搜寻数组来查找某一值或搜寻某一文件表
查找某一雇员的识别卡时,没有找到某一值时将其值赋为False,而一旦找到这个值。则把Found
值赋为True。
    Success。用Success来表示某一操作是否已成功地完成。当某一程序无法完成时,将Success
的值置为False;而当某一操作已经完成时,将Success的值置为True。如果可能的话,可以用
比Success 更准确更具有表达力的名称来代替它,用这个新名称应可以精确地表达出到底是什
么已 成功地完成了。比如当某一程序完成处理后就可以认为是成功时,就可以用
Processingcomplete 来代替Success。如果当找到某一值就可以为程序是成功的时,可以用Found
来代替它。
gototop
 
«910111213141516   13  /  16  页   跳转
页面顶部
Powered by Discuz!NT