1.1 介绍
本文档详细说明了Wind River的C代码和包含在代码中的附加文档的编写习惯。 这些习惯是特意约定的,不全,但是能写出更高质量的代码。每一个源模块必须包含某些基本的文档,并且这些代码和文档必须具备一个好的格式以便于阅读和理解。
这些习惯也用来在不同的程序员之间提供一个统一的编码标准,这样就能减少在别人的代码上工作时因为不同的代码风格所做的额外调整。同时,也能允许对代码的自动处理。比如,可以写一些程序来自动生成参考手册,模块摘要,更新说明等。
本文档所描述的编码习惯由以下两部分组成:
l 文件标题:无论什么编程语言,在每一个源文件的头部都有一个统一的标题说明
l C代码习惯
1.2 文件标题
每一个包含C代码的文件,不管它是头文件,资源文件,或者是一个使用主机工具、库例程或应用程序的文件,都必须包含一个标准的文件标题。
文件标题由下面描述的几块组成,这些块由一个或几个空行分开,但是在每一个块里面不能有空行,以便于文件标题的自动处理。
l 名称:由一行注释组成,包含工具,库或应用程序的名字,以及一个简短的描述。名字必须跟文件名一样。这一行将变成自动处理后的参考条目和索引中的名称。
l 版权:由一行注释组成,包含适当的版权信息。
l 更改历史:包含一个注释块,就是C语言中的多行注释。更改历史中的每一个条目包含版本号、更改时间、更改人和有关更改内容的完整描述。
版本号由两个阿拉伯数字和一个字符组成(例如,03C)。字符的增加表示一个内部的改动(小的改动),数字的增加表示一个大的改动,特别是改变了模块的外部接口。
下面是一个标准的C源文件的文件标题的例子:
Example I-1: Standard File Heading (C Version)
/* fooLib.c - foo subroutine library */
/* Copyright 1984-1995 Wind River Systems, Inc. */
/*
modification history
--------------------
02a,15sep92,nfs added defines MAX_FOOS and MIN_FATS.
01b,15feb86,dnw added routines fooGet() and fooPut();
added check for invalid index in fooFind().
01a,10feb86,dnw written.
*/
1.3 C代码习惯
分成下面几个种类
l 模块布局
l 子程序布局
l 代码布局
l 命名习惯
l 风格
l 头文件布局
l 文档生成
1.3.1 C模块布局
一个模块是指在一个单一文件中的所有代码单元。模块标题紧跟在文件标题之后,由以下几块组成,每一块之间由一个或几个空行隔开。
在更新历史之后和第一个函数或可执行的模块代码之前,如果需要,必须按照顺序包含下面的几个部分:
l 模块文档概要:由注释块组成,包含所有模块目的和功能的一个完整的描述,特别是外部接口。包括相关的包含头文件。格式为INCLUDE FILES:文件列表。
l 包含头文件:由一个包含includes字样的单行注释加上一行或多行C的预处理宏#include组成。 这部分包含了本模块需要包含的所有头文件。
l 定义:由一个包含defines字样的单行注释加上一个或多个C的预处理宏#define组成。这部分包含了本模块所需要的所有定义。
l 类型:由一个包含typedefs字样的单行注释加上一个或多个C的typedef声明组成。这部分包含了本模块需要的所有类型定义。
l 全局变量:由一个包含globals字样的单行注释加上一个或多个C的全局定义组成,每行一个。这部分包含了所有希望在本模块外能访问的变量的定义。
l 本地变量:由一个包含local字样的单行注释加上一个或多个C的局部定义组成,每行一个。这部分包含了所有不希望在本模块外能访问的变量的定义。
l 前向声明:由一个包含forward declarations字样的单行注释加上一个或多个ANSI C的函数原型声明组成,每行一个。这部分包含了所有本模块中定义的函数原型。前向声明仅仅用于本地函数,其它类型的函数在头文件中声明。
下面是一个例子,包含文件标题:
Example I-2: C File and Module Headings
/* fooLib.c - foo subroutine library */
/* Copyright 1984-1995 Wind River Systems, Inc. */
/*
modification history
--------------------
02a,15sep92,nfs added defines MAX_FOOS and MIN_FATS.
01b,15feb86,dnw added routines fooGet() and fooPut();
added check for invalid index in fooFind().
01a,10feb86,dnw written.
*/
/*
DESCRIPTION
This module is an example of the Wind River Systems C coding conventions.
...
INCLUDE FILES: fooLib.h
*/
/* includes */
#include "vxWorks.h"
#include "fooLib.h"
/* defines */
#define MAX_FOOS 112 /* max # of foo entries */
#define MIN_FATS 2 * min # of FAT copies */
/* typedefs */
typedef struct fooMsg /* FOO_MSG */
{
VOIDFUNCPTR func; /* pointer to function to invoke */
int arg [FOO_MAX_ARGS]; /* args for function */
} FOO_MSG;
/* globals */
char * pGlobalFoo; /* global foo table */
/* locals */
LOCAL int numFoosLost; /* count of foos lost */
/* forward declarations */
LOCAL int fooMat (list * aList, int fooBar, BOOL doFoo);
FOO_MSG fooNext (void);
STATUS fooPut (FOO_MSG inPar);
1.3.2 C子程序布局
每一个子程序前面由一段C的注释标题组成,包含以下的几块。标题中不能有空行,但是每一块必须由第一个字符是(*)且仅包含这个字符的单独一行分开。
l Banner:表示注释的开始,由一个斜线(/)和75个星号(*)组成。
l 名称:只有一行,包含子程序名和一段短的注释。名称中的子程序名必须匹配声明中的子程序名。这一行将变成自动处理后的参考条目和索引中的名称。
l 描述:一个完整的描述,关于这个子程序的功能和如何使用。
l 返回值:单词RETURNS:加上可能的返回值的描述。如果没有返回值(声明时返回值为void),输入:RETURNS: N/A
l 错误号:单词ERRNO:加上所有可能返回的错误号。不用给出错误的描述,只需要写出错误号的值和常量定义的形式。
子程序标题用C语言的字符(*/)结束,必须另起一行,并从第一列开始。
Example I-3: Standard C Subroutine Layout:
/********************************************************************
*
* fooGet - get an element from a foo
*
* This routine finds the element of a specified index in a specified
* foo. The value of the element found is copied to <pValue>.
*
* RETURNS: OK, or ERROR if the element is not found.
*
* ERRNO:
* S_fooLib_BLAH
* S_fooLib_GRONK
*/
STATUS fooGet
(
FOO foo, /* foo in which to find element */
int index, /* element to be found in foo */
int * pValue /* where to put value */
)
{
...
}
1.3.3 C定义格式
一行只包含一个定义,并且必须符合当前的缩进水平。
变量
l 基本变量:首先是类型,然后插入一个制表符(TAB),最后是包含在一个单行注释中的描述。例如:
unsigned rootMemNBytes; /* memory for TCB and root stack */
int rootTaskId; /* root task ID */
BOOL roundRobinOn; /* boolean for round-robin mode */
l 指针变量的定义格式如下
FOO_NODE * pFooNode; /* foo node pointer */
FOO_NODE ** ppFooNode; /* pointer to the foo node pointer */
l 结构的格式:第一行是关键字struct加上结构名,下一行是开始的大括号,然后就是结构中的元素。每一个元素占用单独的一行,并采用适当的缩进和注释。如果需要,注释可以不止一行。定义的结尾是一个反大括号,类型名字,分号。记住,始终用typedef来声明这个结构或联合,并且包含结构名和类型名。不要直接用结构定义去声明一个变量。例:
typedef struct symtab /* SYMTAB - symbol table */
{
OBJ_CORE objCore; /* object maintanance */
HASH_ID nameHashId; /* hash table for names */
SEMAPHORE symMutex; /* symbol table mutual exclusion sem */
PART_ID symPartId; /* memory partition id for symbols */
BOOL sameNameOk; /* symbol table name clash policy */
int nSymbols; /* current number of symbols in table */
} SYMTAB;
这个格式也适用于其他的复合类型声明,例如联合或枚举。
不直接用结构定义去声明一个变量的例外是在结构中有一个指向结构的指针,隐
含着另外一种类型声明。这种情况允许结构存储相关的结构指针而不需要包含定义那种类型的头文件。
例如,下面的例子可以编译通过而不需要包含定义类型struct fooInfo的头文件
| |||||||||||||||||||
CORRECT: | typedef struct tcbInfo
{
struct fooInfo * pfooInfo;
...
} TCB_INFO;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |||||||||||||||||||
|
相反,下面的例子如果不包含定义类型FOO_INFO的头文件就不能通过编译。
| |||||||||||||||||||
INCORRECT: | typedef struct tcbInfo
{
FOO_INFO * pfooInfo;
...
} TCB_INFO;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
子程序:
子程序的声明有两种格式,取决于子程序是否带参数。
l 如果子程序带参数,第一行是返回类型和子程序名,第二行是括号,接着就是传给子程序的参数,每行一个,最后包含一个反括号的单独一行表示子程序声明的结束。例如:
int lstFind
(
LIST * pList, /* list in which to search */
NODE * pNode /* pointer to node to search for */
)
l 如果子程序不带参数,就只占用一行的空间,并且括号之间必须有一个关键字void。例如:
STATUS fppProbe (void)