2007年9月21日星期五

开发规范参考

开发规范参考

修订历史
修订 0.9.1 2003/07/16 jiangxin
网友 falls huang 对变量命名提出修改意见。
修订 0.9 2002/01/11 jiangxin
转换到 Docbook 格式
修订 0.8 2001/10/24 jiangxin
增加编程规范
修订 0.7 2001/08/23 jiangxin
用indent格式化

摘要

编程风格分强制和建议两种。

强制风格,意为一定需要遵守,推荐风格,意为希望程序员在编码时按照这样的风格。

(编译自版本: 65,最后更新时间: 2007-06-19 17:40:06)


1. 箴言

箴言警句一起共勉。

  • 程序设计绝对是一门艺术,而不仅仅是一门技术。

  • 首先程序设计的出发点是,是给别人看,可读、易理解、好维护,如果你的程序只能自己来维护,到你离开这个程序时,你程序也就与你一起离开了这个世界。

  • 为了可读、易理解、好维护,你的程序要有好的设计,而不是一接手就进行东抄抄、西抄抄的写代码工作。

  • 写代码是一个工程,程序设计是一种艺术;如果程序员只注重代码,那就象盖房子时的砌砖匠,只知道照图施工而已。世界那么多令人叹为观止的美丽建筑物,那是设计家的艺术杰作,而不是砌砖匠的艺术杰作。

  • Keep It Easy Read and Right (from ibm readbooks)

  • Sometimes good style and efficient runtime performance do not mix. Wherever you face a conflict between the two, choose good style. Hard-to-read programs are

    hard to debug,
    hard to maintain,
    and hard to get right.

  • Program correctness must always win out over speed.

    Make it right before you make it faster.

    Keep it right when you make it faster.

    Make it clear before you make it faster.

    Do not sacrifice clarity for small gains in efficiency.

  • Keep It Simle and Stupid

    Knowledge Intergration is Supper Skill
    What I hear I forget,
    What I see I remember,
    What I do I understand.
  • whatever style you use, please use it consistently, since a mixture of styles within one program tends to look ugly. If you are contributing changes to an existing program, please follow the style of that program.

2. 命名规范

2.1. 版本号命名

Linux 下的系统开发,受到 Linux 核心编号的影响,很可能采用 <主版本号>.<次版本号>.<修订号> 来命名自己产品的编号。Linux 核心还有一个约定,就是如果次版本号是偶数(如0、2、4等),代表正式版本,如果次版本号是奇数(如1、3、5等),代表的是开发过程中的测试版本。修 订号则相当于 Build 号,用来标识一些小的改动。

Windows 下的开发,则可能受到资源文件中 FILEVERSION,PRODUCTVERSION 定义的启发,采用四位版本号。

但无论是类似 Linux 的三位版本号还是类似 Windows 程序的四位版本号,一定要能够从版本号反推出源代码的版本号。这就需要有完备的编译管理,使得版本号最后一位的编译号(build number)随每次编译能够自动加一,在 《Nightly Build Howto》 中有介绍。

2.2. 变量命名

2.2.1. 规则

变量(还包括宏)的命名规则,比较系统和彻底的有 Windows 编程中用到的匈牙利命名法。匈牙利命名法通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等。这些符号可以多个同时使 用,顺序是先m_(成员变量),再指针,再简单数据类型,再其他。例如:m_lpszStr, 表示指向一个以0字符结尾的字符串的长指针成员变量。

[提示]

有关匈牙利命名法的一点有意思的说明是它的名字的由来。这种命名技术是由一 位能干的 Microsoft 程序员查尔斯·西蒙尼(Charles Simonyi) 提出的,他出生在匈牙利。在 Microsoft 公司中和他一起工作的人被教会使用这种约定。这对他们来说一切都很正常。但对那些 Simonyi 领导的项目组之外的人来说却感到很奇特,他们认为这是死板的表达方式,甚至说代有这样奇怪的外观是因为它是用匈牙利文写的。从此这种命名方式就被叫做匈牙 利命名法。

匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途。

匈牙利命名法中常用的小写字母的前缀
前  缀: a
类  型: 数组 (Array)
前  缀: b
类  型: 布尔值 (Boolean)
前  缀: by
类  型: 字节 (Byte)
前  缀: c
类  型: 有符号字符 (Char)
前  缀: cb
类  型: 无符号字符 (Char Byte,没有多少人用)
前  缀: cr
类  型: 颜色参考值 (ColorRef)
前  缀: cx,cy
类  型: 坐标差(长度 ShortInt)
前  缀: dw
类  型: Double Word
前  缀: fn
类  型: 函数
前  缀: h
类  型: Handle
前  缀: i
类  型: 整型
前  缀: l
类  型: 长整型 (Long Int)
前  缀: lp
类  型: Long Pointer
前  缀: m_
类  型: 类的成员
前  缀: n
类  型: 短整型 (Short Int)
前  缀: np
类  型: Near Pointer
前  缀: p
类  型: Pointer
前  缀: s
类  型: 字符串型
前  缀: sz
类  型: 以null做结尾的字符串型 (String with Zero End)
前  缀: w
类  型: Word

但是在任何情况下,都硬性规定使用匈牙利命名法是迂腐的。尤其是 Unix 编程,在使用没有变量名、关键字自动补齐功能的编辑器,如 vi 下,去敲入大小写混合的变量名是痛苦的。其实只要注意两个原则:1. 含义清晰,不易混淆; 2. 不和其它模块、系统API的命名空间相冲突即可。

  1. 有意识的为变量名、宏名加上本模块的关键字,就不至于和其它模块、系统API的命名空间相冲突;

    例如: 宏的名称过短,如:"DEBUG" 或 "_DEBUG",很可能和别的模块,系统模块相冲突;

  2. 局部变量尤其是循环变量外,使用约定俗成的 i,j,k ,没有问题;

  3. 宏、常量、枚举enum,全部用大写字母;

  4. 全局变量加上前缀 "g",后面跟上首字母大写的单词;

    全局变量吗,费事一点可以谅解。

2.2.2. 实例

略...

2.3. 函数命名

2.3.1. 规则

  1. 属于某一模块的函数,加上前缀,前缀为模块缩写;

  2. 函数名应该表明函数意义,格式为"前缀_名词_动词";

2.3.2. 实例

略...

2.4. 版本控制系统的TAG/Label命名

  1. CVS 的 TAG 不能包括如 "." 的特殊字符,因此规定使用由大写字母、数字、下划线、减号组成版本控制系统的 TAG/LABEL 名称;

  2. TAG/LABEL 要包含一个模块/工程前缀,如果只是简单的 V1,V2,在合并 Repository 时,造成困惑;

  3. 在版本控制系统中建立一个专门记录 TAG/LABEL 的文件,每次打上 TAG 之时,先向该文件追加(append)一行,内容为新的 TAG 名称,CHECKIN,在为代码打上 TAG/LABEL;

    原因为 CVS 中要查看到 TAG 的创建时间不方便,用此方法便于跟踪;

  4. 还可以在 TAG 名称中加入建立 TAG 的日期,如果这个 TAG 是一个 STICKY TAG,日后不会改动;

  5. 分支 TAG/LABEL 的名称要和其它 TAG 容易区分,如加上前缀 "B-"。

3. 格式规范

3.1. 一般格式原则

3.1.1. 规则

  1. 缩进用tab,不用空格;

  2. 一个tab定义为四个空格,原因见后面表格;

  3. 标点符号的右边留一个空格(左括号除外);

  4. 赋值、判断、运算符号两边都要加一个空格;

  5. 空行也是注释,适当的空行使逻辑更清楚,函数内部不应该有连续空行,函数之间至少有两个连续空行;

3.1.2. 实例

略...

3.2. 变量

  1. 当出现变量和常量比较和判断时,常量放在左边,以防止"= ="错误输入为"=",时可以由编译器检验;(强制)

  2. 变量定义块分两部分,外部定义在前,且加上extern关键字;

  3. 程序的include 块也分两部分;一部分为包含标准头文件,后一部分为本工程内部定义的头文件;

3.3. 函数

  1. 所有调用函数在".h"头文件中声名,对于只是在本模块中调用的函数,用static关键字限制;

  2. main函数,一定有返回值,且为int类型;

  3. 函数之间空两行,以示区分;

  4. 函数的类型和函数名分开两行写,即函数类型和函数名都从1列开始;

    方便使用命令 grep "^函数名" 文件名 快速查找函数定义,而不会定位到函数调用的代码;

  5. 函数的参数一个一行(推荐);

  6. 一个函数不长于100行左右,即不超过两屏,如果超过,说明应该考虑进一步模块化;

3.4. 注释

应把注释看做和代码一样重要,也要统计到代码量中去。注释除了能够使得代码更易读、清晰外,还能用来生成文档。参见:Doxygen 计划

  • 分类

    1. 按位置分:

      1. 位于语句后;2. 变量声明后;3. 予处理后;4. 或单独开始的注释(又有是否从第一行开始的区别)。

    2. 按照形式分:

      块注释;单行注释(又有是否独立一行或者在代码后面的区别)。

      块注释(boxed comments):Boxed comments are defined as those in which the initial `/*' is followed immediately by the character `*', `=', `_', or `-', or those in which the beginning comment delimiter (`/*') is on a line by itself, and the following line begins with a `*' in the same column as the star of the opening delimiter.

    3. 以上各个类型的注释可以被indent工具区分和格式化;indent忽略块注释,对齐单行注释等等;

    [提示]

    For larger blocks of code that I want to comment and un-comment regularly (eg, debug code), I like to use comments in this style:

    /* debug code ------------- //
    echo "This is debug code";
    ...
    // ---------------------- */

    You can easily uncoment the whole block by changing the /* to //, and vice versa.

  • 书写规范

    1. 可以用c++风格注释"//",或者用c风格"/* */";

    2. 注释最好用英文,英文困难找翻译;

    3. 文件头注释结构:为块注释。参见:Doxygen 的风格

    4. 函数前注释结构:为块注释。注名功能。如果参数名不能表达意义,还要说明参数意义。

    5. 块注释写法:

         /*
      * a space before *
      */

      或者

         /* have a space after
      three spaces before */

3.5. 用 indent 进行代码格式化

一个范例,胜过前言万语。用 indent 格式化代码,再从格式化前后的格式变化, 来学习格式化规范。

可以用不同的参数调用 indent,

3.5.1. introduce indent

  1. What is Indent

    The `indent' program can be used to make code easier to read. It can also convert from one style of writing C to another.

  2. Download

    Current Version: GNU indent 2.2.7。 Download : ftp.gnu.org/gnu/indent/indent-2.2.7.tar.gz

3.5.2. Indent参数规范

我们参照GNU,Kernighan & Ritchie,Berkeley风格,制定了自己风格:

  1. indent命令参数:

    -bad -bap -bbb -bbo -nbc -bl -bli0 -bls -c33 -cd33 -ncdb -ncdw -nce -cli0 -cp33 -cs -d0 -nbfda -di2 -nfc1 -nfca -hnl -ip5 -l75 -lp -pcs -nprs -psl -saf -sai -saw -nsc -nsob -nss -i4 -ts4 -ut

  2. indent配置文件

    如上参数可写入用户目录下的文件:".indent.pro",作为运行indent的确省参数。

  3. indent配置说明

    表 1. Indent代码格式化说明

    使用的indent参数 含义
    --blank-lines-after-declarations bad 变量声明后加空行
    --blank-lines-after-procedures bap 函数结束后加空行
    --blank-lines-before-block-comments bbb 块注释前加空行
    --break-before-boolean-operator bbo 较长的行,在逻辑运算符前分行
    --blank-lines-after-commas nbc 变量声明中,逗号分隔的变量不分行
    --braces-after-if-line bl "if"和"{"分做两行
    --brace-indent 0 bli0 "{"不继续缩进
    --braces-after-struct-decl-line bls 定义结构,"struct"和"{"分行
    --comment-indentationn c33 语句后注释开始于行33
    --declaration-comment-columnn cd33 变量声明后注释开始于行33
    --comment-delimiters-on-blank-lines ncdb 不将单行注释变为块注释
    --cuddle-do-while ncdw "do --- while"的"while"和其前面的"}"另起一行
    --cuddle-else nce "else"和其前面的"}"另起一行
    --case-indentation 0 cli0 switch中的case语句所进0个空格
    --else-endif-columnn cp33 #else, #endif后面的注释开始于行33
    --space-after-cast cs 在类型转换后面加空格
    --line-comments-indentation n d0 单行注释(不从1列开始的),不向左缩进
    --break-function-decl-args nbfda 关闭:函数的参数一个一行
    --declaration-indentationn di2 变量声明,变量开始于2行,即不必对齐
    --format-first-column-comments nfc1 不格式化起于第一行的注释
    --format-all-comments nfca 不开启全部格式化注释的开关
    --honour-newlines hnl Prefer to break long lines at the position of newlines in the input.
    --indent-leveln i4 设置缩进多少字符,如果为tab的整数倍,用tab来缩进,否则用空格填充。
    --parameter-indentationn ip5 旧风格的函数定义中参数说明缩进5个空格
    --line-length 75 l75 非注释行最长75
    --continue-at-parentheses lp 续行从上一行出现的括号开始
    --space-after-procedure-calls pcs 函数和"("之间插入一个空格
    --space-after-parentheses nprs 在"("后")"前不插入空格
    --procnames-start-lines psl 将函数名和返回类型放在两行定义
    --space-after-for saf for后面有空格
    --space-after-if sai if后面有空格
    --space-after-while saw while后面有空格
    --start-left-side-of-comments nsc 不在生成的块注释中加*
    --swallow-optional-blank-lines nsob 不去掉可添加的空行
    --space-special-semicolon nss 一行的for或while语句,在";"前不加空。
    --tab-size ts4 一个tab为4个空格(要能整除"-in")
    --use-tabs ut 使用tab来缩进

indent详细参数以及各种编程排版风格见: 附录

4. Makefile

  1. 使用autoconf/automake/autoheader工具

    用autoconf/automake/autoheader工具来处理各种移植性的问题,用这些工具 完成系统配置信息的收集,制作makefile文件。然后在打算编译源码时只需要通过 “configure; make”这样简单的命令就可以得到干净利落的编译。

  2. GNU Makefile

    参见 GNU Makefile和configure

  3. BSD Makefile

    参见 FreeBSD Porter's Handbook

5. 用DocBook来写文档

参见:《Docbook Howto》

A. 不同编程风格参考

通过indent参数分析,比较自定义风格和GNU,KR,BSD编程风格。

表 A.1. 自定义风格和GNU,KR,BSD风格比较

参数 含义 我们的风格 GNU风格 KR风格 BSD风格
-bad --blank-lines-after-declarations y n n n
-bap --blank-lines-after-procedures y y y n
-bbb --blank-lines-before-block-comments y


-bbo --break-before-boolean-operator y y y y
-bc --blank-lines-after-commas n n n y
-bl --braces-after-if-line y y

-blin --brace-indent n 0 2

-bls --braces-after-struct-decl-line y y

-br --braces-on-if-line

y y
-brs --braces-on-struct-decl-line

y y
-bs --blank-before-sizeof



-cn --comment-indentationn 33
33 33
-cbin --case-brace-indentationn



-cdn --declaration-comment-columnn 33
33 33
-cdb --comment-delimiters-on-blank-lines n n n y
-cdw --cuddle-do-while



-ce --cuddle-else n n n y
-cin --continuation-indentationn

4 4
-clin --case-indentationn 0
0 0
-cpn --else-endif-columnn 33 1 33 33
-cs --space-after-cast y y y
-dn --line-comments-indentationn 0
0
-ndj indents declarations the same as code
y

-bfda --break-function-decl-args n


-din --declaration-indentationn 2 2 1 16
-fc1 --format-first-column-comments n n n y
-fca --format-all-comments n n n y
-gnu --gnu-style



-hnl --honour-newlines y y y y
-in --indent-leveln 4 2 4 4
-ipn --parameter-indentationn 5 5 0 4
-kr --k-and-r-style



-ln --line-lengthn 75
75 75
-cs --space-after-cast



-dn --line-comments-indentationn



-bfda --break-function-decl-args



-din --declaration-indentationn



-fc1 --format-first-column-comments



-fca --format-all-comments



-gnu --gnu-style



-hnl --honour-newlines



-in --indent-leveln



-ipn --parameter-indentationn



-kr --k-and-r-style



-ln --line-lengthn



-lcn --comment-line-lengthn



-lp --continue-at-parentheses y
y y
-lps --leave-preprocessor-space



-orig --original



-npro --ignore-profile



-pcs --space-after-procedure-calls y y n n
-pin --paren-indentationn



-pmt --preserve-mtime



-prs --space-after-parentheses n n n n
-psl --procnames-start-lines y y n y
-saf --space-after-for y y y y
-sai --space-after-if y y y y
-saw --space-after-while y y y y
-sbin --struct-brace-indentationn



-sc --start-left-side-of-comments n n n y
-sob --swallow-optional-blank-lines n n n n
-ss --space-special-semicolon n
n n
-st --standard-output



-T typenames Tell indent the name of typenames.



-tsn --tab-sizen 4

8
-ut --use-tabs y


-v --verbose



-version Output the version number of indent.




$Date: 2007-06-19 17:40:06 +0800 (二, 19 6月 2007) $

没有评论: