1 / 48

程序设计实践

程序设计实践. 程序风格. 参考教材 《 程序设计实践 》 第 1 章. 主要内容. 标识符命名 表达式和语句 一致性和习惯 函数宏 神秘的数 注释 编程规范介绍. 风格的作用. 具有良好风格的代码更容易阅读和理解,几乎可以保证其中的错误更少。 程序不仅需要给计算机读,也要给人读。 好的风格使代码更容易读,无论是对程序员本人,还是其他读程序的人。 风格是一种习惯,通过规范形式给出。. P 1 例:. if ( ( country == SING ) || ( country == BRNI ) ||

kiril
Download Presentation

程序设计实践

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 程序设计实践 程序风格 参考教材《程序设计实践》第1章

  2. 主要内容 • 标识符命名 • 表达式和语句 • 一致性和习惯 • 函数宏 • 神秘的数 • 注释 • 编程规范介绍

  3. 风格的作用 • 具有良好风格的代码更容易阅读和理解,几乎可以保证其中的错误更少。 • 程序不仅需要给计算机读,也要给人读。 • 好的风格使代码更容易读,无论是对程序员本人,还是其他读程序的人。 • 风格是一种习惯,通过规范形式给出。

  4. P1例: if ( ( country == SING ) || ( country == BRNI ) || ( country == POL ) || ( country == ITALY ) { /* * if the country is Singapore, Brunei or Poland * then the current time is the answer time * rather than off hook time. * Reset answer time and set day of week. */ …… }

  5. P2例: #define ONE 1 #define TEN 10 #define TWENTY 20 #define INPUT_MODEL 1 #define INPUT_BUFSIZE 10 #define OUTPUT_BUFSIZE 20

  6. 程序设计语言的风格不相同: • 编程风格因人而异。 • 每种程序设计语言都有自己特殊的风格。 • 有一些规则是所有程序设计语言都应遵守的。

  7. 1.1 名字 • 名字,又可以称为标识符,它可以是变量名、函数名、标号、宏名、常量名,甚至文件名。 • 对名字的一般要求: • 简洁、容易记忆 • 符合大多数人的习惯 • 最好能够拼读 • 最好具有一定的含义

  8. 名字的规则: • 全局变量使用具有说明性的名字 • 足够长,足够的说明性,加注释 • 局部变量用短名字 • 保持一致; • 函数采用动作性名字(含有动词); • 更准确(携带有助读程序的信息)

  9. 1.1名字(续1) • 全局(说明性) vs. 局部(短名字) eg.1 记录队列长度 int npending = 0; //current length of queue 反例 eg.2 局部计数器变量 int n vs. int npoints / numberofpoints • 通常,i、j作为循环变量,p、q作为指针,s、t表示字符串等,过长了反而有害

  10. 典型的命名法 • 匈牙利命名法--Simonyi • prefix + basename • basename使用大小写混排 • 例如: char chType; char *pszName; • 其他命名法参考: • http://www.cppblog.com/issayandfaye/archive/2010/03/11/109424.html

  11. 1.1名字(续2) • 改进: • struct UserQueue • { • int nitems, front, capacity; • int (*nusers)(); • } • queue.capacity++; • n = (*queue.nusers)(); • C中可以使用函数指针成员; • 需要进行合适的初始化; • 保持一致性 • 统一名字指示同类对象=> • eg.4 struct UserQueue { int noOfItemsInQ, frontOfTheQueue, queueCapacity; int (*noOfUsersInQueue)(); } len = queue.queueCapacity;

  12. 名字(续3) • 函数使用动作性名字---动词+名词 • 易于理解 eg.5 获取当前时间 now = date.getTime(); putchar('\n'); eg.6 判断字符是否8进制数 if (checkoctal(c)) ... vs. if (isoctal(c)) ..√

  13. 名字(续4) • 准确---符合表达的信息 eg.7 判断是否为8进制数组成字符 #define isoctal(c) ((c) >= '0' && (c) <= '8') vs. #define isoctal(c) ((c) >= '0' && (c) <= '7') eg.8 名字与实现一致 boolean inTable(Object *this, Object obj) { int j = this->getIndex(obj); return (j == nTable); }

  14. 1.2 表达式和语句 • 表达式具有运算功能,简单形式可以只有一个常量或一个变量,复杂时可能超过几行。 • 表达式的好风格关键是让读者明白操作符与操作数的关系,清晰是最重要的。 • 语句最关键的是缩行,体现出语句的层次关系。

  15. 表达式的规则 • 用缩行显示程序的结构 • 使用表达式的自然形式 • 用加括号的方式排除二义性 • 分解复杂的表达式 • 更清晰,写出最清晰的代码,而不是最巧妙的代码。 • 当心副作用

  16. 表达式和语句(续一) • 一致的缩行风格 • 进行必要缩行,同级代码起始于相同列 • 符合语句的一般形式要求

  17. 表达式和语句(续二) eg.1 初始化字符数组 for(n++;n<100;field[n++]='\0'); *i='\0';return('\n'); vs. for (n++; n < 100; n++) field[n] = '\0'; *i = '\0'; return '\n';

  18. 表达式和语句(续三) • 自然的表达式 eg.2 if (!(block_id < actblks) || !(block_id >= unblocks)) ... vs. if ((block_id >= actblks) || (block_id < unblocks)) ...

  19. 表达式和语句(续四) • 充分使用括号---消除二义性 • 控制运算的顺序 eg.3 赋值 vs. 逻辑运算 while ((c = getchar()) != EOF) ... eg.4 位运算 vs. 逻辑运算 if (x&MASK == BITS) // <=> if (x && (MASK ==BITS)) vs. if ((x & MASK) == BITS)

  20. 表达式和语句(续五) • 容易理解 eg.5 判断是否为闰年 leap_year = y % 4 == 0 && y % 100 != 0 || y % 400 == 0; vs. leap_year = ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);

  21. Oh my god! 表达式和语句(续六) • 分解复杂的表达式 --- 不要太长 eg.6 *x += (*xp=(2*k < (n-m)? c[k+1]: d[k--])); vs. if (2*k < (n-m)) *xp = c[k+1]; else *xp = d[k--]; *x += *xp;

  22. Oh my god! 表达式和语句(续七) • 清晰---最巧妙未必最清晰 eg.7 获得移位数目 subkey = subkey >> (bitoff - ((bitoff >>3) << 3)) vs. subkey >>= bitoff & 0x7 显示内层表达式右移三位,然后又重新移回来。目的是后三位置0,而再用原值bitoff减去它,目的是取bitoff的最低的三位。 再由此值判断subkey的右移位数

  23. Oh my god! 表达式和语句(续八) eg.8 反对滥用?: --- 源代码少  执行码少 child = (!LC &&!RC)? 0 : (!LC?RC:LC); vs. if (LC == 0 && RC == 0) child = 0; else if (LC == 0) child = RC; else child = LC; 运算符?: 适用于短的表达式, 不应该成为条件语句的一般替代品

  24. Oh! NO! 表达式和语句(续九) • 注意副作用 • 考虑两方面:符合习惯 vs 不确定性 • 保证单语句中的副作用与计算顺序无关 eg.9 向str中连续写入两个空格 str[i++] = str[i++] = ' '; vs str[i++] = ' '; str[i++] = ' ';

  25. 表达式和语句(续十) eg.10 scanf的参数 scanf("%d %d", &yr, &profit[yr]); vs scanf("%d", &yr); scanf("%d", &profit[yr]);

  26. 练习1-4 改进下面各个程序片段 if ((c != 'y') && (c != 'Y’)) return; if ( !( c == 'y' || c == 'Y' ) ) return; length = ( length < BUFSIZE ) ? length : BUFSIZE; flag = flag ? 0:1; quote = ( *line == '"') ? 1 : 0; if ( val & 1 ) bit = 1; else bit = 0; if ( length >= BUFSIZE ) length =BUFSIZE; quote = ( *line == toascii(34)) ? 1 : 0; bit = ( val & 1 ) ? 1 : 0;

  27. 1.3 一致性和习惯用法 • 一致性带来的将是更好的程序。 • 如果相同的计算的每次出现总是采用相同的方式,任何变化就预示着是经过深思熟虑,要求读程序的人注意。 • 如果你工作在一个不是自己写的程序上,请注意保留程序原有的风格。当你需要做修改时,不要使用你自己的风格,即使你特别喜欢它。

  28. 一致性的一般要求: • 使用一致性的缩排和加括号风格 • 为一致性,使用习惯用法 • 用else-if表达多路选择

  29. 一些习惯用法:(1/4) • 给n个元素的数组赋初值 for( i = 0; i<100; i++ ) array[i] = 1.0; • 无穷循环 • 写法1: for ( ; ; ) …… • 写法2: whlie( 1 ) …... while (i <= n-1) array[i++] = 1.0; for(i = 0; i < n; ) array[i++] = 1.0

  30. 一些习惯用法:(2/4) • 赋值放在循环里 while ( ( c = getchar( ) ) != EOF ) putchar( c ); • 字符串分配空间及操作的习惯 p=malloc( strlen( buf ) + 1 ) ; strcpy( p, buf );

  31. 一些习惯用法:(3/4) • 用else-if表达多路选择 if ( condition1 ) statement1 else if ( condition2 ) statement2 …… else if ( condition ) statementn else default-statement

  32. 例:处理命令行参数 (差) • if (argc == 3) • if ((fin = fopen(argv[1], "r")) != NULL) • if ((fout = fopen(argv[2], "w")) != NULL) • { • while((c = getc(fin)) != EOF) • putc(c, fout); • fclose(fin); fclose(fout); • } • else • printf("Can't open output file \n"); • else • printf("Can't open input file \n"); • else • printf("Usage: cp inputfile outputfile \n");

  33. if (argc != 3) • printf("Usage: cp inputfile outputfile \n"); • else if ((fin = fopen(argv[1], "r")) == NULL) • printf("Can't open input file \n"); • else if ((fout = fopen(argv[2], "w")) == NULL) • { • printf("Can't open output file \n"); • fclose(fin); • } • else • { • while((c = getc(fin)) != EOF) • putc(c, fout); • fclose(fin); • fclose(fout); • }

  34. 一些习惯用法:(4/4) • switch-case语句中break特别重要 switch ( c ) { case '-': sign = -1; /* fall through */ case '+': c = getchar( ); case '.': break; default: …… }

  35. 练习1-7 if ( istty( stdin ) ) ; else if (istty( stdout ) ); else if ( istty( stderr ) ); else return( 0 ); if ( reval != SUCCESS ) { return ( reval ); } /* All went well! */ return SUCCESS; for ( k=0; k++<5; x+=dx ) scanf( "%lf", &dfx ); if ( !istty( stdin ) || !istty( stdout ) || ( !istty( stderr ) ) return( 0 ); if ( reval != SUCCESS ) return ( reval ); else return SUCCESS; for ( k=0; k<5; k++, x+=dx ) scanf( "%lf", &dx );

  36. 找出下面程序段中的错误并按习惯修改循环 int count = 0; while (count < total) { count++; if (!strcmp(getName(count), nametable.userName)) { return (true); } }

  37. 1.4 函数宏 • 函数宏的主要问题是,它的副作用,如: #define isupper(c) ( (c) >= 'A' && (c) <='Z' ) …… while( isupper( c = getchar( ) ) ) 上述程序改写成: #define isupper(c) ( (c) >= 'A' && (c) <='Z' ) …… while(( c = getchar( )) != EOF && isupper( c ))

  38. 减少函数宏副作用的方法: • 给宏的体和参数都加上括号。如下写法: #define square( x ) ( x ) * ( x ) …... y = 1 / square( x ); ↓ y = 1 / ( x ) * ( x ); 给宏的体和参数都加上括号: # define square( x ) ( ( x ) * ( x ) )

  39. 解决宏函数的一般方法: • 给宏的体和参数都加上括号 • 避免使用宏函数,要用函数或inline函数,替换宏函数。 • 在目前的计算机处理能力下,函数宏的缺点远远超过它能带来的好处。

  40. 1.5 神密的数 • 神密的数是这样一些数字,读程序的人很难从数字本身得到它所表示的含义。 • 神密的数包含: • 各种常数 • 数组的大小 • 字符位置 • 系数 • ……。 • 解决神密数的好方法是给它们起名字。

  41. P15例:(1/3) fac = lim / 20; /* set scale factor */ if ( fac < 1 ) fac = 1; /* generate histogram */ for ( i = 0, col = 0; i < 27; i++, j++ ) { col += 3; k = 21 - ( let[i] / fac ); star = ( let[i] == 0 ) ? ' ' : '*'; for ( j = k; j < 22; j++ ) draw( j, col, star ); } draw( 23, 2, ' ' ); /* label x axis */ for ( i = 'A'; i <= 'Z'; i++ ) printf( "%c ", i );

  42. 改写后的程序:(2/3) enum { MINROW = 1, /* top edge */ MINCOL = 1, /* left edge */ MAXROW = 24, /* bottom edge */ MAXCOL = 80, /* right edge (<=) */ LABELROW= 1, /* position of labels */ NLET = 26, /* size of alphabet */ HEIGHT = MAXROW - 4, /* height of bars */ WIDTH = ( MAXCOL - 1 ) / NLET /* width of bars */ };

  43. 改写后的程序:(3/3) fac = ( lim+HEIGHT-1) / HEIGHT; /* set scale factor */ if ( fac < 1 ) fac = 1; for (i=0; i<NLET; i++ ) { /* generate histogram */ if ( let[i] == 0 ) continue; for ( j=HEIGHT - let[i] / fac; j<HEIGHT; j++ ) draw( j+1+LABELROW, ( i+1 )*WIDTH, '*' ); } draw( MAXROW-1, MINCOL + 1); /* label x axis */ for ( i = 'A'; i <= 'Z'; i++ ) printf( "%c ", i );

  44. 解决神密数的一般方法 • 把数定义为常数,不要定义为宏 • 使用字符形式的常数,不要用对应的整数 • 利用语言去计算对象的大小,如sizeof

  45. 1.6 注释 • 注释是帮助程序读者的一种手段。 • 最好的注释是简洁地点明程序的突出特征,或者提供概括说明,帮助别人理解程序。 • 注释包括: • 序言注解 • 功能注解 • 语句注解

  46. 练习1-11 评论下面的注释: if ( n > MAX || n % 2 > 0 ) //test for even number //Write a message //Add to line counter for each line written void write_message( ) { // increment line counter line_number = line_number + 1; fprintf( fout, "%d %s\n%d %s\n%d %s\n", line_number, HEADER, line_number + 1, BODY, line_number + 2, TRAILER ); // increment line counter line_number = line_number + 2 ; }

  47. 注释的一般原则 • 不要大谈明显的东西。 • 给函数和全局数据加注释。 • 不要注释差的代码,重写它。 • 不要与代码矛盾。 • 澄清情况,不要添乱。

  48. 程序风格的关键: 好风格应该成为一种习惯。 如果你在开始写代码时就关心风格问题,如果你花时间去审视和改进它,你将会逐渐养成一种好的编程习惯。一旦这种习惯变成自动的东西,你的潜意识就会帮你照料许多细节问题,甚至你在工作压力下写出的代码也会更好。

More Related