如何让C++中的代码规范一点
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何让C++中的代码规范一点相关的知识,希望对你有一定的参考价值。
1.关键字if, while, for与其后的控制表达式的(括号之间插入一个空格分隔,但括号内的表达式应紧贴括号。例如:while ␣(1);
2.双目运算符的两侧插入一个空格分隔,单目运算符和操作数之间不加空格。
例如:i␣=␣i␣+␣1、++i、!(i␣<␣1)、-x、&a[1]等。
3.后缀运算符和操作数之间也不加空格。
例如:取结构体成员s.a、函数调用foo(arg1)、取数组成员a[i]。
4. 、,号和;号之后要加空格,这是英文的书写习惯。
例如:for␣(i␣=␣1;␣i␣<␣10;␣i++)、foo(arg1,␣arg2)。
5.以上关于双目运算符和后缀运算符的规则不是严格要求,有时候为了突出优先级也可以写得更紧凑一些.
例如:for␣(i=1;␣i<10;␣i++)、distance␣=␣sqrt(x*x␣+␣y*y)等。但是省略的空格一定不要误导了读代码的人,例如a||b␣&&␣c很容易让人理解成错误的优先级。
6.由于标准的Linux终端是24行80列的,接近或大于80个字符的较长语句要折行写,折行后用空格和上面的表达式或参数对齐。
例如: if␣(sqrt(x*x␣+␣y*y) > 5.0
&&␣x␣<␣0.0
&&␣y␣>␣0.0)
缩进
内核关于缩进的规则有以下几条:
1.要用缩进体现出语句块的层次关系,使用Tab字符缩进,不能用空格代替Tab。函数里面的代码,也称为代码块或复合代码,要求进行缩进。遇到循环和分支结构的处理,循环和分支下的代码块要求再进行缩进,假设循环和分支里又嵌套了循环和分支,代码块应该层层缩进
2.if/else、while、do/while、for、switch这些可以带语句块的语句,语句块的和应该和关键字写在一起,用空格隔开,而不是单独占一行。
例如应该这样写:
if␣(...)␣
→语句列表
␣else␣if␣(...)␣
→语句列表
更多的人习惯这样写:
If ␣(…)
→语句列表
else if ␣(…)
→语句列表
内核的写法和[K&R]一致,好处是不必占用太多空行,使得一屏能显示更多代码。这两种写法用得都很广泛,只要在同一个项目中能保持统一就可以了。
3.函数定义的和单独占一行,这一点和语句块的规定不同.
例如 :
int sum(int a, int b)
语句列表;
4. switch和语句块里的case、default对齐写,也就是说语句块里的case、 default相对于switch不往里缩进。
例如:
switch(…)
case ‘A’:
语句列表;
case ‘B’:
语句列表;
default:
语句列表;
5.一行只写一条语句
6.代码中每个逻辑段落之间应该用一个空行分隔开。例如每个函数定 义之间应该插入一个空行,头文件、全局变量定义和函数定义之间也应该插入空行.
7.在分支和循环中不管有一条还是多条语句建议都要加上“”
注释
1.单行注释应采用/*␣comment␣*/的形式,用空格把界定符和文字分开。
2.整个源文件的顶部注释。说明此模块的相关信息,例如文件名、作者和版本历史等,顶头写不缩进。
3.相对独立的语句组注释。对这一组语句做特别说明,写在语句组上侧,和此语句组之间不留空行,与当前语句组的缩进一致。注意,说明语句组的注释一定要写在语句组上面,不能写在语句组下面。
4.代码行右侧的简短注释。对当前代码行做特别说明,一般为单行注释,和代码之间至少用一个空格隔开,一个源文件中所有的右侧注释最好能上下对齐。
标识符的命名规范
标识符的命名要清晰明了,可以使用完整的单词和大家易于理解的缩写。短的单词可以通过去元音形成缩写,较长的单词可以取单词的头几个字母形成缩写,也可以采用大家基本认同的缩写。例如count写成cnt,block写成blk,length写成len,window写成win,message写成msg,temporary可以写成temp,也可以进一步写成tmp。
内核风格规定变量、函数和类型采用全小写加下划线的方式命名,常量(宏定义和枚举常量)采用全大写加下划线的方式命名。如函数名radix_tree_insert、类型名struct radix_tree_root。
常量的命名规范:每一个英文字符大写 ,每个单词之间可以用‘_’连接 RADIX_TREE_MAP_SHIFT等。
标签的命名规范
要求有一定的描述性,顶端左对齐
全局变量和全局函数的命名一定要详细,不惜多用几个单词多写几个下划线,例如函数名radix_tree_insert,因为它们在整个项目的许多源文件中都会用到,必须让使用者明确这个变量或函数是干什么用的。局部变量和只在一个源文件中调用的内部函数的命名可以简略一些,但不能太短,不要使用单个字母做变量名,只有一个例外:用i、j、k做循环变量是可以的。
针对中国程序员的一条特别规定:禁止用汉语拼音作为标识符名称,可读性极差。
函数的编码风格
每个函数都应该设计得尽可能简单,简单的函数才容易维护。应遵循以下原则:
实现一个函数只是为了做好一件事情,不要把函数设计成用途广泛、面面俱到的,这样的函数肯定会超长,而且往往不可重用,维护困难。
函数内部的缩进层次不宜过多,一般以少于4层为宜。如果缩进层次太多就说明设计得太复杂了,应该考虑分割成更小的函数来调用。
函数不要写得太长,建议在24行的标准终端上不超过两屏,太长会造成阅读困难,如果一个函数超过两屏就应该考虑分割函数了。
执行函数就是执行一个动作,函数名通常应包含动词,例如get_current、radix_tree_insert。
比较重要的函数定义上面必须加注释,说此函数的功能、参数、返回值、错误码等。
另一种度量函数复杂度的办法是看有多少个局部变量,5到10个局部变量就已经很多了,局部变量再多就很难维护了,应该考虑分割函数。
函数参数的防错设计
程序一般分为Debug 版本和Release 版本,Debug 版本用于内部调试,Release 版本发行给用户使用。
在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的假定,就要使用断言对假定进行检查。使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。
在函数的入口处,使用断言检查参数的有效性(合法性)。
assert(表达式); 的意思是:当表达式为真时,程序继续运行,如果表达市为假,那程序就会停止运行,并提示错误信息。
#include <stdio.h>
#include <assert.h>
void test(char *p)
assert(p != NULL);
printf("p=%s\n",p);
程序说明:由于我们在main函数中传了NULL指针值给test函数,在test函数执行到assert(p != NULL);发现表达式不为真,就终止了程序的运行,并提示错误的行数信息。
注意:由于assert是一个宏,只在debug版本中起作用,所以assert一般只用于内部函数对参数有效性进行检查,如果该函数作为一个外部接口来使用时,一般需要利用if,else语句进行防错设计。
函数注释:说明此函数的功能、参数、返回值、错误码等,写在函数定义上侧,和此函数定义之间不留空行,顶头写不缩进。
/*-------------------------------------------------
function:判断分数格式是否正确
params:score 客户输入的分数
returns:1 分数格式正确 0 分数格式错误
-------------------------------------------------*/
int IsValidScore(char *score)
/*……*/
函数内的注释要尽可能少用。注释只是用来说明你的代码能做什么(比如函数接口定义),而不是说明怎样做的,只要代码写得足够清晰,怎样做是一目了然的,如果你需要用注释才能解释清楚,那就表示你的代码可读性很差,除非是特别需要提醒注意的地方才使用函数内注释。
编程好习惯
初始化变量
尽可能使用局部变量
一个函数只有一个紧凑的职责
避免函数过长避免函数嵌套过深
不要拘泥于小节
…… 参考技术A C/C++源代码书写规范
1. 在.h/.cpp的开头应有一段格式统一的说明,内容包括:
a. 文件名 (FileName);
b. 创建人 (Creator);
c. 文件创建时间 (Date);
d. 简短说明文件功能、用途 (Comment)。
例:
//////////////////////////////////////////
//
// FileName: GetVersion.h
// Creator: Liu Haifeng
// Date: 2002-10-28
// Comment: Get file version information
//
//////////////////////////////////////////
2.对于主要功能函数应有注释说明。内容包括:功能、入口/出口参数,必要时还可有备注或补充说明,对于函数中的主要功能代码,也应有注释说明。注释以英文为主。
例:
//////////////////////////////////////////
//
//Remarks:
// Check file whether exist
//
//Parameters:
// LPCTSTR lpszPathName : Input full path name
//
//Return values:
// int : Non-zero means success
// Zero means failure
//
//////////////////////////////////////////
3. 每列代码的长度推荐为80列,最长不得超过120列;折行以对齐为准。
例:HANDLE KSOpenFile(const char cszFileName[],
int nMode);
或者:
BOOL KSReadFile(
HANDLE hFile,
void *pvBuffer,
int nReadSize,
int *pnReadSize
);
4. 循环、分支代码,判断条件与执行代码不得在同一行上。
例:正确:
if (n == -2)
n = 1;
else
n = 2;
不得写做:
if (n == -2) n = 1;
else n = 2;
5. 指针的定义,* 号既可以紧接类型,也可以在变量名之前。
例:可写做:int* pnsize;
也可写做:int *pnsize;
但不得写做:int * pnsize;
6. 在类的成员函数内调用非成员函数时,在非成员函数名前必须加上"::"。
例:::Sleep(2000);
7. 函数入口参数有缺省值时,应注释说明。
例:
BOOL KSSaveToFile(
const char cszFileName[],
BOOL bCanReplace /* = TRUE */
);
或者:
BOOL KSSaveToFile(
const char cszFileName[],
BOOL bCanReplace // = TRUE
);
8. else if 必须写在一行。
9. 与‘’有关的各项规定:
9.1‘’应独占一行。在该行内可有注释。
例:正确:
for (i = 0; i < cbLine; i++)
// .....
printf("Line %d:", i);
printf("%s\n", pFileLines);
不得写做:
for (i = 0; i < cb; i++)
printf("Line %d:", i);
printf("%s\n", pFileLines);
9.2‘’必须在同一列上。
例:正确:
if (i > 0)
m = 1;
n++;
不得写做:
if (i > 0)
m = 1;
n++;
例外:
if (i == 0)
9.3 在循环、分支之后若只有一行代码,虽然可省略‘’,但不推荐这么做。若省略后可能引起歧义,则必须加上‘’。
例:正确:
if (n == -2)
n = 1;
else
n = 2;
或者:
if (n == -2)
else if (n != nTemp)
else
不得写做:
if (n == -2)
n = 1;
else if (n != nTemp)
n = 2;
else
n = 3;
不推荐:
if (i < 1)
else
if (i == 1)
else
if (i > 1)
10. 与空格有关的各项规定。
10.1 在所有两目、三目运算符的两边都必须有空格。在单目运算符两端不必空格。但在‘->’、‘::’、‘.’、‘[’、‘]’等运算符前后,及‘&’(取地址)、‘*’(取值)等运算符之后不得有空格。
例:正确:
int n = 0, nTemp;
for (int i = nMinLine; i <= nMaxLine; i++)
不得写做:
int n=0, nTemp;
for ( int i=nMinLine; i<=nMaxLine; i++ )
10.2 for、while、if 等关键词之后应有1个空格,再接‘(’,之后无空格;在结尾的‘)’前不得有空格。
例:正确:
if (-2 == n)
不得写做:
if(-2 == n)
或
if ( -2 == n )
等等。
10.3 调用函数、宏时,‘(’、‘)’前后不得有空格。
例:正确:
printf("%d\n", nIndex);
不得写做:
printf ("%d\n", nIndex);
printf( "%d\n", nIndex );
等等。
10.4 类型强制转换时,‘(’‘)’前后不得有空格
例:可写做:
(KSFile*)pFile;
也可写做:
(KSFile *)pFile
不得写做:
( KSFile* )pFile
( KSFile * ) pFile
11. 与缩进有关的各项规定
11.1 缩进以 Tab 为单位。1 个 Tab 为 4 个空格
11.2 下列情况,代码缩进一个 Tab:
1. 函数体相对函数名及''。
例:
int Power(int x)
return (x * x);
2. if、else、for、while、do 等之后的代码。
3. 一行之内写不下,折行之后的代码,应在合理的位置进行折行。若有 + - * / 等运算符,则运算符应在上一行末尾,而不应在下一行的行首。
11.3 下列情况,不必缩进:switch 之后的 case、default。
例:
switch (nID)
case ID_PLAY:
......
break;
case ID_STOP:
......
break;
default:
......
break;
12.对于涉及到系统的功能调用,要求使用系统提供的宏,不得直接使用具体的数值。
例: char szSystemPath[MAX_PATH] = ; 正确
char szSystemPath[260] = ; 错误
13.关于函数编写的规定
13.1 除了非常简单的函数或者没有必要,函数需要有返回值,并且推荐
返回值的类型为int,成功时为 true,失败时为false。不推荐直接使用1和0来表示。
13.2 在函数内部变量定义全部放在函数的开始处,不能在代码中间出现 变量定义。变量定义需要初始化值,一般是0、false、NULL等。
13.3 程序员自行编写的函数不推荐使用DWORD等指明字节长度的变量 类型,推荐使用unsigned long等变量类型。对于ULONG等vc系统定义的变量类型,推荐使用unsigned long 来代替。如果调用系统API,在MSDN中已经指明了是DWORD等类型,则可以使用。
13.4 推荐使用int nResult作为函数自身的返回值变量,int nRetCode、BOOL bRetCode作为调用其他函数的返回值变量。
13.5 推荐使用goto语句来统一函数出口,goto语句一般只能跳转到一个函数出口,最多只能有两个,出口标识必须命名为Exit0和Exit1。在出口处必须显式检查曾经申请过的系统资源(如内存),并且进行释放。
13.6 如果编写DLL,除非特殊情况,导出函数的调用类型必须是__stdcall,便于多模块协同工作。
附录:
1.函数编写示范
/////////////////////////////////////////////////////////////////////
//
// Remarks:
// Get file version information
//
// Parameters:
// [in] const char cszPathName : Input file path name
// [out] PVERSION_INFO *pVersionInfo : File version information
//
// Return values:
// int : Non-zero means success, zero means failure
//
/////////////////////////////////////////////////////////////////////
int GetFileVersion(const char cszPathName, PVERSION_INFO *pVersionInfo)
int nResult = false;
int nRetCode = false;
char szSystemPath[MAX_PATH] = ;
nRetCode = IsFileExist(cszPathname);
if (!nRetCode)
goto Exit0;
//Others code here
nResult = true;
Exit0:
return nResult;
本回答被提问者采纳
如何让python调用C和C++代码
如何让python调用C和C++代码安装python后,会有一个chm格式的python手册。要搞明白如何让python调用C/C++代码(也就是写python的 extension),你需要征服手册中的
<<Extending && embedding>>厚厚的一章。在昨天花了一个小时看地头晕脑胀,仍然不知道如何写python的extension后,查阅了一些其他 书籍,最终在<<Python Programming On Win32>>书中找到了教程。
下面记录一下如何在visual studio 2005中,写一段C/C++的MessageBox代码,然后提供后python调用,最后的结果当然是显示一个MessageBox.
1. 首先要明白的是,所谓的python扩展(也就是你提供给python的c/c++代码,不一定是c/c++代码,可以是其他语言写的代码)是一个 dll,并且这个dll放在本机python安装目录下的DLLs目录下(譬如我机器上的路径是:F:\Program Files\Python25\DLLs),假如我们接下来要写的扩展module名为mb,python调用的代码为: import mb
mb.showMsg("Python's really amazing, I kindda love it!")
python怎么找到我们的mb模块呢?就是上面说的,我们要生成一个mb.dll,然后拷贝到Dlls目录下面,为了区别普通的dll和python专用扩展的dll,我们的 mb.dll修改成mb.pyd(python dll)
2. 搭建环境,我们要使用python提供的c头文件和lib库来进行扩展的开发。 在vs 2005下点击菜单 "工具"->"选项", 打开选项对话框,选择"项目和解决方案->VC++目录", 然后在右边"显示以下内容的目录"得comboBox上选择"包含文件”,添加python的include目录(我的机器上是"F:\Program
Files\Python25\include"),然后选择库文件,添加python的libs目录(我的机器上是"F:\Program Files\Python25\libs")。
既然扩展是一个dll,接下来我们要建立一个“动态链接库”工程,然后开始写代码:
#include <python.h> //python.h是包含python一些定义的头文件,在python的include目录下 /*
我的python版本是2.5, 因为安装python后它没提供debug下的lib库文件,因此你必须生成release版的dll,
想要生成dll版本的,你要到python官网上自己去下载python源代码,当然你可以继续生成release版本的dll,但dll中包含调试信息
*/
#pragma comment(lib, "python25.lib")
//先不管
static PyObject* mb_showMsg(PyObject* self, PyObject *args); /*
如果你的扩展是mb,那么必须实现一个initmb函数,并且从dll中导出这个函数,但我们在python中调用import mb时,python会去dll里去调用
initmb函数,这个函数告诉python我们有些什么函数,该怎么告诉python我们有一个showMsg函数呢?下面详解 */
//必须extern "C"下,这样不会在C++编译器里不会更改掉导出的函数名字,我第一次就犯了这样的错误
extern "C" __declspec(dllexport) void initmb() /*
当调用mb.showMsg("Python's really amazing, I kindda love it!")时, 相当于你告诉python我有一个showMsg函数,我们怎么告诉python去调用我们dll里的mb_showMsg函数呢?技巧就是下面的方式, 定义一个字典数据结构,key => showMsg, value =>mb_showMsg,METH_VARARGS是函数调用方式,仔细查手册吧 */
static PyMethodDef mbMethods[] = "showMsg", mb_showMsg, METH_VARARGS,
NULL, NULL, NULL /*sentinel,哨兵,用来标识结束*/ ;
//告诉python我们的模块名叫mb, 模块包含的函数都在mbMethods字典里 PyObject *m = Py_InitModule("mb", mbMethods); /*
接下来实现核心功能showMsg */
//第一个self参数我们用不着,具体查手册,第二个参数是python传给我们的参数,它是一个python的参数tuple
static PyObject* mb_showMsg(PyObject* self, PyObject *args)
//我们的showMsg函数需要的是一个字符串参数 const char* msg = NULL; /*
调用特殊参数解码python传递给我们的参数,s是string,我们传递接收参数的变量地址,
如果你的功能函数需要两个参数,在PyArg_parseTuple后面继续添加接受参数的变量地址,
这个函数的原型是类似printf的不定参数的形式
PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...); */
if (!PyArg_ParseTuple(args, "s", &msg)) return NULL;
//调用MB
int r = ::MessageBox(NULL, "hello", "Caption:Form C module", MB_ICONINFORMATION | MB_OK);
//返回值
return Py_BuildValue("i", r);
将上面这段混杂着大量注释的代码拷贝到你的编辑器里,然后编译生成mb.dll,修改后缀成mb.pyd,然后拷贝到python的DLLs目录下,打开idle(python的交互程序),写入代码: import mb
mb.showMsg("Python's really amazing, I kindda love it!")
可以看到弹出来一个MessageBox。 参考技术A 解决方案
如果你的功能函数需要两个参数;.pyd,s是string;包含文件”,
模块包含的函数都在mbMethods字典里
PyObject *m = Py_InitModule(".showMsg("/:F;s really
amazing;第一个self参数我们用不着;C" showMsg,python调用的代码为,你要到python官网上自己去下载python源代码, NULL
/Extending &&
embedding>!PyArg_ParseTuple(args;项目和解决方案->)时要搞明白如何让python调用C/先不管static
PyObject* mb_showMsg(PyObject* self, &msg))
return
NULL?技巧就是下面的方式;,修改后缀成mb,你需要征服手册中的<显示以下内容的目录", MB_ICONINFORMATION |
MB_OK);)
2,然后选择库文件,并且从dll中导出这个函数;<c++代码,打开idle(python的交互程序):Form C
module", I kindda love it;C++代码(也就是写python的extension), NULL,METH_VARARGS是函数调用方式,
I kindda love it:MessageBox(NULL;Python'。
既然扩展是一个dll;*我的python版本是2;/if
(:
#include <将上面这段混杂着大量注释的代码拷贝到你的编辑器里;Python25/Program
Files/,选择"python25;/.), ", r)。
1,但dll中包含调试信息*/include"hello",
mb_showMsg;返回值return Py_BuildValue(",添加python的include目录(我的机器上是";
__declspec(dllexport) void
initmb()/,
这个函数的原型是类似printf的不定参数的形式
PyAPI_FUNC(int)
PyArg_ParseTuple(PyObject *;// /,我们传递接收参数的变量地址;->;libs"Caption;)。
在vs
2005下点击菜单 ",但我们在python中调用import mb时;s really amazing,然后开始写代码;VC++目录"Python25/.
首先要明白的是;Program Files//showMsg"<,python会去dll里去调用
extern
",定义一个字典数据结构,所谓的python扩展(也就是你提供给python的c/c++代码;;调用MBint r =
;i".showMsg(".h>,我们怎么告诉python去调用我们dll里的mb_showMsg函数呢;Python',
想要生成dll版本的;/python,
PyObject *args);*接下来实现核心功能showMsg*/:/,可以是其他语言写的代码)是一个dll,key =>*/.,
"。在昨天花了一个小时看地头晕脑胀;告诉python我们的模块名叫mb;Python25/>,不一定是c/,因此你必须生成release版的dll;书中找到了教程,然后拷贝到python的DLLs目录下;我们的showMsg函数需要的是一个字符串参数
const
char* msg = NULL,添加python的libs目录(我的机器上是"DLLs):import
mbmb;*调用特殊参数解码python传递给我们的参数,
NULL;/, 然后在右边"/static PyMethodDef mbMethods[]
=
", ".,具体查手册,在PyArg_parseTuple后面继续添加接受参数的变量地址;/!"*当调用mb;s really amazing,
,它是一个python的参数tuple
static PyObject* mb_showMsg(PyObject*
self.h是包含python一些定义的头文件;),
"!"*如果你的扩展是mb,并且这个dll放在本机python安装目录下的DLLs目录下(譬如我机器上的路径是:import mbmb;工具",
mbMethods),写入代码;Python', PyObject *args)/, METH_VARARGS,
因为安装python后它没提供debug下的lib库文件;选项"*sentinel,第二个参数是python传给我们的参数;python,假如我们接下来要写的扩展module名为mb;/;/.dll;F,当然你可以继续生成release版本的dll,
打开选项对话框;,仔细查手册吧*/,我们要使用python提供的c头文件和lib库来进行扩展的开发. 搭建环境;mb_showMsg,用来标识结束*/Program
Files/"/,然后编译生成mb, value
=>:,接下来我们要建立一个“动态链接库”工程;mb",那么必须实现一个initmb函数,仍然不知道如何写python的extension后.showMsg("#pragma
comment(lib:/得comboBox上选择".5,在python的include目录下/s", const char *,
相当于你告诉python我有一个showMsg函数,查阅了一些其他书籍;厚厚的一章;,最终在</!";Python Programming On
Win32>, I kindda love it;)/
以上是关于如何让C++中的代码规范一点的主要内容,如果未能解决你的问题,请参考以下文章