C语言编程要点---第19章编程风格和标准.doc_第1页
C语言编程要点---第19章编程风格和标准.doc_第2页
C语言编程要点---第19章编程风格和标准.doc_第3页
C语言编程要点---第19章编程风格和标准.doc_第4页
C语言编程要点---第19章编程风格和标准.doc_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C语言编程要点-第19章编程风格和标准第19章 编程风格和标准本章集中讨论程序的布局,包括注释和空白符的使用,变量和函数的命名标准,使用大括号的技巧等内容。在本章中,你将会了解到:使用注释和空白符不会影响程序的速度、大小和效率;在代码中加大括号的三种格式;有关变量和函数命名的两种命名法(骆驼式和匈牙利式);在变量名和函数名中加下划线可增加其可读性;怎样为函数命名以及函数名和变量名应该有多长。 除了有关命名的约定和标准外,本章还将讨论这样一些常见的编程问题:递归(它是什么以及如何使用它);空循环;无穷循环;通过while,dowhile和for循环进行重复处理;continue语句和break语句的区别;在程序中表示真和假的最好办法。 本章涉及的内容很多,你要耐心学习,并且一定要认真学习命名的格式和约定,这些内容有助于加强你的程序的可读性。19.1. 应该在变量名中使用下划线吗? 在变量名中使用下划线是一种风格。使用或完全不使用下划线都没有错误,重要的是要保持一致性在整个程序中使用相同的命名规则。这就是说,如果你在一个小组环境中编程,你和其它小组成员应该制定一种命名规则。并自始至终使用这种规则。如果有人使用了别的命名规则,那么集成的程序读起来将是很费劲的。此外,你还要与程序中用到的第三方库(如果有的话)所使用的风格保持一致。如果可能的话,你应该尽量使用与第三方库相同的命名规则,这将加强你的程序的可读性和一致性。 许多C程序员发现在命名变量时使用下划线是很方便的,这可能是因为加下划线会大大加强可读性。例如,下面的两个函数名是相似的,但使用下划线的函数名的可读性更强: check disk space available(selected disk drive); CheckDiskSpaceAvailable (Selected Disk Drive); 上例中的第二个函数名使用了骆驼式命名法见195中关于骆驼式的解释。 请参见: 192 可以用变量名来指示变量的数据类型吗? 195 什么是骆驼式命名法? 196 较长的变量名会影响程序的速度、大小或效率吗? 199 一个变量名应该使用多少个字母?ANSI标准允许有多少个有效字符? 1910 什么是匈牙利式命名法?应该使用它吗?19.2. 可以用变量名来指示变量的数据类型吗? 可以。在变量名中指出数据类型已经成为今天的大型复杂系统中普遍使用的一条规则。通常,变量类型由一个或两个字符表示,并且这些字符将作为变量名的前缀。使用这一技术的一种广为人知的命名规则就是匈牙利命名法,它的名称来自于Microsoft公司的程序员CharlesSimonyi。表192列出了一些常用的前缀。 表1 92一些常用的匈牙利命名法前缀-数据类型 前缀 例子-char c clnCharint i iReturnValuelong l lNumRecsstring sz szlnputString ( 以零字节结束 )int array ai aiErrorNumberschar * psz pszInputString- 象Microsoft Windows这样的环境,就大量使用了匈牙利命名法或其派生体。其它一些第四代环境,例如Visual Basic和Access,也采用了匈牙利命名法的一种变体。 在编写程序时,你不必拘泥于一种特定的命名法你完全可以建立自己的派生命名法,特别是在为自己的typedef命名时。例如,有一个名为SOURCEFILE的typedef,用来保存源文件名、句柄、行号、最后编译日期和时间、错误号等信息。你可以引入一个类似“sf”(sourcefile)的前缀符号,这样,当你看到一个名为sfBuffer的变量时,你就会知道该变量保存了SOURCEFILE结构中的部分内容。 不管怎样,在命名变量或函数时,引入某种形式的命名规则是一个好主意,尤其是当你与其它程序员共同开发一个大的项目时,或者在Microsoft Windows这样的环境下工作时。采用一种认真设计好的命名规则将有助于增强你的程序的可读性,尤其是当你的程序非常复杂时。 请参见: 191 应该在变量名中使用下划线吗? 195 什么是骆驼式命名法? 196 较长的变量名会影响程序的速度、大小或效率吗? 199 一个变量名应该使用多少个字母?ANSI标准允许有多少个有效字符? 1910 什么是匈牙利式命名法?应该使用它吗?19.3. 使用注释会影响程序的速度、大小或效率吗? 不会。当你的程序被编译时,所有的注释都会被忽略掉,只有可执行的语句才会被分析,并且被放入最终编译所得的程序版本中。 因为注释并不会给程序的速度、大小或效率带来负担,所以你应该尽量多使用注释。你应该在每一个程序模块的头部都加上一段注释,以解释该模块的作用和有关的特殊事项。同样,你也要为每一个函数加上注释,其中应包括作者姓名、编写日期、修改日期和原因、参数使用指导、函数描述等信息。这些信息将帮助其它程序员更好地理解你的程序,也有助于你以后回忆起一些关键的实现思想。 在源代码中也应该使用注释(在语句之间)。例如,如果有一部分代码比较复杂,或者你觉得有些地方要进一步说明,你就应该毫不犹豫地在这些代码中加入注释。这样做可能会花费一点时间,但这将为你或其它人节省几个小时的宝贵时间,因为只需看一下注释,人们就能马上知道你的意图。 在194中有一个例子,它表明了使用注释、空白符和下划线命名规则是如伺使程序更清晰、更易懂的。 请参见: 194 使用空白符会影响程序的速度、大小或效率吗? 196 较长的变量名会影响程序的速度、大小或效率吗?19.4. 使用空白符会影响程序的速度、大小或效率吗? 不会。与注释一样,所有的空白符都会被编译程序忽略掉。当你的程序被编译时,所有的空白符都会忽略掉,只有可执行的语句才会被分析,并且被放入最终编译所得的程序版本中。 在C程序中用空白符隔开可执行语句、函数和注释等,将有助于提高程序的可读性和清晰度。许多时候,只要在语句之间加入空行,就可提高程序的可读性。请看下面的这段代码: / * clcpy by GBlansten * / void clcpy(EMP * e,int rh,int ot) e-grspy= (e-rt * rh)+ (e-rt * ot * 1.5) ; e-txamt = e-grspy * e-txrt ; e-ntpy = e-grspy-e-txamt ; updacctdata (e); if (e-dd= =false) cutpyck(e) ; else prtstb (e) ; 你可以看到,这个函数确实是一团糟。尽管这个函数显然是正确的,但恐怕这个世界上没有一个程序员愿意去维护这样的代码。如果采用本章中的一些命名规则(例如使用下划线,少用一些短而模糊的名字),使用一些大括号技巧,加入一些空白符和注释,那么这个函数将会是下面这个样子:/*/Function Name: calc_payParameters: emp -EMPLOYEE pointer that points to employee data reg-hours -The number of regular hours (40) employee has workedAuthor : Gern BlanstenDate Written: 13 dec 1993Modification: 04 sep 1994 by Lloyd E. Work -Rewrote function to make it readable by human beings.Description: This function calculates an employees gross bay ,tax amount, and net pay, and either prints a paycheck for the employee or (in the case of those who have direct deposit) prints a paycheck stub./*/void calc_pay (EMPLOYEE * emp, int reg hours, int or_hours) / * gross_pay = (employee rate * regular hours)+ (employee rate * overtime hours * 1.5) * / emp-gross_pay= (emp-rate * reg_hours) + (emp-rate * ot hours* 1.5); / * tax amount=gross_pay * employees tax rate * / emp-tax amount=emp-gross_pay * emp-tax-rate ; / * net pay=gross pay-tax amount * / emp-net-pay=emp-gross pay-emp-tax_amount ; / * update the accounting data * / update accounting data(emp); / * check for direct deposit * / if (emp-direct_deposit= =false) cut_ paycheck(emp); / * print a paycheck * / else print_paystub(emp); /* print a paycheck stub * / 你可以看到,Lloyd版本(该版本中使用了大量的注释、空行、描述性变量名等)的可读性比糟糕的Gern版本要强得多。 你应该尽量在你认为合适的地方使用空白符(和注释符等),这将大大提高程序的可读性一一当然,这可能会延长你的工作时间。请参见:193 使用注释会影响程序的速度、大小或效率吗?196 较长的变量名会影响程序的速度、大小或效率吗?19.5. 什么是骆驼式命名法? 骆驼式命令法,正如它的名称所表示的那样,是指混合使用大小写字母来构成变量和函数的名字。例如,下面是分别用骆驼式命名法和下划线法命名的同一个函数: PrintEmployeePaychecks(); print employee paychecks(); 第一个函数名使用了骆驼式命名法函数名中的每一个逻辑断点都有一个大写字母来标记;第二个函数名使用了下划线法-函数名中的每一个逻辑断点都有一个下划线来标记。 骆驼式命名法近年来越来越流行了,在许多新的函数库和Microsoft Windows这样的环境中,它使用得当相多。另一方面,下划线法是c出现后开始流行起来的,在许多旧的程序和UNIX这样的环境中,它的使用非常普遍。 请参见: 191 应该在变量名中使用下划线吗? 192 可以用变量名来指示变量的数据类型吗? 196 较长的变量名会影响程序的速度、大小或效率吗? 199 一个变量名应该使用多少个字母?ANSI标准允许有多少个有效字符? 1910 什么是匈牙利式命名法?应该使用它吗?19.6. 较长的变量名会影响程序的速度、大小或效率吗? 不会,当你的程序被编译时,每一个变量名和函数名都会被转换为一个“符号”对原变量或原函数的一种较短的符号性的表示。因此,无论你使用下面的哪一个函数名,结果都是一样的: PrintOutAllTheClientsMonthEndReports(); prt_rpts(); 一般说来,你应该使用描述性的函数名和变量名,这样可以加强程序的可读性。你可以查阅编译程序文档,看一下允许有多少个有效字符,大多数ANSI编译程序允许有至少31个有效字符。也就是说,只有变量名或函数名的前31个字符的唯一性会被检查,其余的字符将被忽略掉。 一种较好的经验是使函数名或变量名读起来符合英语习惯,就好象你在读一本书一样人们应该能读懂你的函数名或变量名,并且能很容易地识别它们并知道它们的大概作用。 请参见: 191 应该在变量名中使用下划线吗? 192 可以用变量名来指示变量的数据类型吗? 193 使用注释会影响程序的速度、大小或效率吗? 194 使用空白符会影响程序的速度、大小或效率吗? 195 什么是骆驼式命名法? 199 一个变量名应该使用多少个字母?ANSI标准允许有多少个有效字符? 1910 什么是匈牙利式命名法?应该使用它吗?19.7. 给函数命名的正确方法是什么? 函数名一般应该以一个动词开始,以一个名词结束,这种方法符合英语的一般规则。下面列出了几个命名比较合适的函数: PrintReports(); SpawnUtilityProgram(); ExitSystem(); Initia|izeDisk(): 请注意,在这些例子中,函数名都以一个动词开始,以一个名词结束。如果按英语习惯来读这些函数名,你会发现它们其实就是: print the reports(打印报告) spawn the utility program(生成实用程序) exit the system(退出系统) initialize the disk(初始化磁盘) 使用动词一名词规则(特别是在英语国家)能有效地加强程序的可读性,并且使程序看起来更熟悉。 请参见: 195 什么是骆驼式命名法? 198 作用大括号的正确方法是什么? 1910 什么是匈牙利式命名法?应该使用它吗?19.8. 使用大括号的正确方法是什么? 在C中,使用大括号的方法无所谓对还是错只要每个开括号后都有一个闭括号,你的程序中就不再会出现与大括号有关的问题。然而,有三种著名的大括号格式经常被使用:Kernighan和Ritchie,Allman,Whitesmiths。下文中将讨论这三种格式。 在C程序设计语言(The C Programming Language)一书中,Brian Kernighan和Dennis Ritchie介绍了他们所使用的大括号格式,这种格式如下所示:if (argceopyfile n) ; exit (1) ;else open_files () ; while (! feof(infile) read_data ( ) ; write_data() ; close files() ; 注意,在Kb&R格式中,开括号总是与使用它的语句在同一行上,而闭括号总是在它所关闭的语句的下一行上,并且与该语句对齐。例如,在上例中,if语句的开括号和它在同一行上,|f语句的闭括号在它的下一行上,并且与它对齐。在与if语句对应的else条件语句以及出现在程序段后部的while语句中,情况也是这样的。下面是用Allman格式书写的同一个例子:if (argccopyfile n) ; exit(1);else open_files ( ); while (! feof(infile) read_data ( ) ; write data(); close_files() ; 注意,在Allman格式中,每个大括号都单独成行,并且开括号和闭括号都与使用它们的语句对齐。 下面是用Whitesmiths格式书写的同一个例子:if (argc copyfilen. ) ; exit(1);else open files () ; while (! feof(infile) read_data() ; write data(); close files () ; 与Allman格式相同,Whitesmiths格式也要求大括号单独成行,但是它们要和它们所包含的语句对齐。例如,在上例中,if语句的开括号是与第一个printf()函数调用对齐的。 不管你使用哪一种格式,一定要保持前后一致这将有助于你自己或其它人更方便地读你的程序。 请参见: 195 什么是骆驼式命名法? 197 给函数命名的正确方法是什么? 1910 什么是匈牙利式命名法?应该使用它吗?19.9. 一个变量名应该使用多少个字母?ANSI标准允许有多少个有效字符? 一般说来,变量名或函数名应该足够长,以有效地描述所命名的变量或函数。应该避免使用短而模糊的名字,因为它们在别人理解你的程序时会带来麻烦。例如,不要使用象这样的短而模糊的函数名: opndatfls(); 而应该使用更长一些的函数名,象下面这样: open data_files(); 或者: OpenDataFiles(); 这对变量名也是同样适用的。例如,与其使用这样一个短而模糊的变量名: fmem 不如将其扩展为完整的描述: free_memory_available 使用扩展了的名字会使程序更易读,更易理解。大多数ANSI编译程序允许有至少31个有效字符即只有变量或函数名的前31个字符的唯一性会被检查。 一种较好的经验是使函数名或变量名读起来符合英语习惯,就好象你在读一本书一样一人们应该能读懂你的函数名或变量名,并且能很容易地识别它们并知道它们的大概作用。 请参见: 191 应该在变量名中使用下划线吗? 192 可以用变量名来指示变量的数据类型吗? 195 什么是骆驼式命名法? 196 较长的变量名会影响程序的速度、大小或效率吗? 1910 什么是匈牙利式命名法?应该使用它吗?19.10. 什么是匈牙利式命名法?应该使用它吗? 匈牙利命名法是由Microsoft公司的程序员Charles Simonyi(无疑是个匈牙利人的后裔)提出的。在这种命名法中,变量名或函数名要加上一个或两个字符的前缀,用来表示变量或函数的数据类型。 这种命名法有许多好处,它被广泛应用于象Microsoft Windows这样的环境中。关于匈牙利命名法的详细解释以及其中的一些命名标准,请参见192。 请参见: 191 应该在变量名中使用下划线吗? 192 可以用变量名指示变量的数据类型吗? 195 什么是骆驼式命名法? 196 较长的变量名会影响程序的速度、大小或效率吗? 199 一个变量名应该使用多少个字母?ANSI标准允许有多少个有效字符?19.11. 什么是重复处理(iterative processing)? 重复处理是指反复执行相同的程序语句,但可能会在满足某个条件时退出循环。c语言提供了一些现成的结构来实现重复处理,例如while循环,dowhile循环和for循环。在这些结构中,当某个条件为真时,预先定义的一批语句将被反复执行。下面是一个重复处理的例子:while (xlO0) y=O; do for(z =O;zlOO;z+) y+ ; while (y1000) ; x+; 在这个例子中,包含在while循环中的语句被执行100次,在while循环中还有一个dowhile循环,在dowhlie循环中的for循环被执行10次;在for循环中,变量y作100次自增运算。因此,语句 y+;总共被执行100,000次(100次while10次dowhile100次for)。然而,在while循环结束时,y并不是100,000,因为每循环1000次后y都会被置为o。 在c程序中,重复处理的应用是非常广泛的,例如你经常要用重复处理来读写数组或文件。下面的程序用重复处理来读入并向屏幕打印你的AUTOEXECBAT文件:# include # include int main(viod) ;int main (void) FILE * autoexec_file ; char buffer250 ; if ( (autoexec_file = fopen ( C : AUTOEXEC. BAT, rt ) ) = = NULL ) printf (stderr,Cannot open AUTOEXEC. BAT file. n) ; exit(1) ; printf(Contents of AUTOEXEC. BAT file : nn ) ; while(! feof(autoexec file) fgets (buffer, 200,autoexee_file) ; printf( %s ,buffer) ; felose (autoexee_file) ; rerun(0) ; 注意,上例用一条while语句来反复地调用fgets()和printf()函数,以读入AUTOEXECBAT文件中的每一行,并把它们打印到屏幕上。这仅仅是如何使用重复处理的例子之一。 请参见: 1912 什么是递归(recursion)?怎样使用递归?19.12. 什么是递归(recursion)?怎样使用递归? 在c语言中,一个调用自身(不管是直接地还是间接地)的函数被称为是递归的(recursive)。你可能不明白究竟为什么一个函数要调用自身,要解释这种情况,最好先举一个例子。一个经典的马上可以举出来的例子就是计算整数的阶乘。为了计算一个整数的阶乘值,你要用这个数(x)乘以它的前一个数(X一1),并且一直乘下去,直到到达1。例如,5的阶乘可以这样来计算: 5*4*3*2*1 如果X是5,你可以把这个算式转换为一个等式: X!=X*(X-1)*(X-2)*(X-3)*(X-4)*1 为了用C语言完成这项计算任务,你可以编写一个名为calc_factorial()的函数,它反复调用自身,每次都把被计算的值减1,直到到达1。下面的例子说明怎样编写这个calc_factorial()函数: #include void main(void); unsigned long calc_factorial(unsigned long x); void main(void) int x=5; printf(The factorial of %d is %ld. n ,x,calc_factorial(x);unsigned long calc_factorial(unsigned long x) if(! x) return 1L ; return(x * calc_factorial(x-1L) ; 在上例中,calc_factorial()在调用自身前要先把x的值减去1。如果x等于O,if语句的条件将为真,calc factorial()将不再被递归调用。因此,当被计算的值到达O时,calc_factorial()作完最后一次递归调用并退出,其返回值为1。返回1是因为任何值都可以安全地和1相乘,并仍能保持其原来的值。如果程序中包含下述语句: x=calc_factorial(5);它将开展为: x=5*(5-1)*(4-1)*(3-1)*(2-1)*1;因此,x将等于5的阶乘,即120。 递归是一个简洁的概念,同时也是一种很有用的手段。但是,使用递归是要付出代价的。与直接的语句(如while循环)相比,递归函数会耗费更多的运行时间,并且要占用大量的栈空间。递归函数每次调用自身时,都需要把它的状态存到栈中,以便在它调用完自身后,程序可以返回到它原来的状态。未经精心设计的递归函数总是会带来麻烦。 如果可能的话,你应该避免使用递归函数。例如,前文中的阶乘函数可以写成下面这种形式:# include void main(void) ;unsigned long calc factorial(unsigned long x);void main (void) int x=5; printf(The factorial of %d is %ld. n ,x ,calc_factorial (x) ;unsigned long calc-factorial(unsigned long x) unsigned long factorial; factorial=x; while (x1L) factorial * =-x; return (factorial); 这个版本的calc_factorial()函数用一个while循环来计算一个值的阶乘,它不仅比递归版本快得多,而且只占用很小的栈空间。 请参见: 1911 什么是重复处理(iterative processing)?19.13. 在C语言中,表示真和假的最好方法是什么? 在c语言中,任何等于零的东西都被认为是假,任何等于非零值的东西都被认为是真,因此,最常见的定义就是假为O,真为1。许多程序中都包含了具有如下定义的头文件: #define FALSE O #define TRUE 1 如果你在编写Windows程序,你应该注意头文件windowsh中的TRUE和FALSE的确切定义。上述真和假的定义方式非常普遍,并且是完全可以接受的,然而,还有其它几种定义方式,请看下例: #define FALSE 0 #define TRUE !FALSE 上例把FALSE定义为0,把TRUE定义为非零值。注意,即使是负数,如-1,也是非零值,因此也被认为是真。 另一种定义方式是建立自己的枚举类型,如Boolean(或者BOOL),如下所示: enurn BOOL FALSE, TRUE ; 正象你所可能已经知道的那样,在缺省情况下,枚举类型的第一个元素被赋值为O,因此,在上述枚举定义中,FALSE被赋值为0,TRUE被赋值为1。与使用符号常量相比,使用枚举类型有它的一些好处,详见56和57中的有关内容。 哪种方法最好呢?这个问题没有一个唯一的答案。如果你在编写一个Windows程序,那么TRUE和FALSE都是已经为定义好的,你没有必要再建立自己的定义,在其它情况下,你可以从前文所介绍的几种方法中选择一种。 请参见: 56 用enum关键字说明常量有什么好处? 57 与用#define指令说明常量相比,用enunl关键字说明常量有什么好处?19.14. 空循环(null loops)和无穷循环(infinite loops)有什么区别? 空循环并不会无休止地进行下去在重复预先指定的次数后,它就会退出循环。无穷循环会无休止地进行下去,并且永远不会退出循环。把空循环和无穷循环对比一下,就能很好地说明它们之间的区别。 下面是一个空循环的例子: for(x=O;x500000;x+); 注意,在上例中,在for循环的闭括号后直接加入了一个分号。正如你可能已经知道的那样,c语言并不要求在for循环后加分号,通常只有包含在for循环中的语句后面才会带分号。在for循环后面直接加入分号(并且不使用大括号),即可建立一个空循环实际上是一个不包含任何语句的循环。在上例中,当for循环执行时,变量x将自增500,000次,而在每一次自增运算期间,没有进行任何处理。 那么,空循环有什么用呢?在大多数情况下,它的作用就是在程序中设置一次暂停。前面的例子将使程序“暂停”一段时间,即计算机数到500,000所需的时间。然而,空循环还有更多的用处,请看下例: while(!kbhit(); 这个例子用一个空循环来等待一次击键操作。当程序需要显示类似Press Any Key ToContinue这样的信息时,这种方法是很有用的(假设你的用户很聪明,不会执着地在键盘上寻找Any Key!)。 无穷循环与空循环不同,它永远不会结束。

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论