编程规范总结

概述

逝者如斯,岁月从指缝间溜走,年华在不觉中老去,弹指一挥间…

从用单片机点亮第一个LED灯,以及用C语言打印出第一个”Hello World!”,已经过去了差不多十年,其间写过成千上万行的代码,看过的代码更是多如牛毛

不知道大家有没有这样的感觉,当看到不规范(杂乱差)的代码,瞬间就失去了胃口,没有了想看下去的欲望!

多年的编程生涯告诉我,编程不仅是一门技术,也是一门艺术。程序可以写的整洁舒适,让人赏心悦目,这就依赖于编程非常重要的两大法宝——编程规范&编程框架,这是编程的两大地基!

本文要说的就是编程规范,也就是编写出简洁、可靠、高效、可测试、可维护、可移植的代码。新手开始写程序也许不能感受到它的重要性,但有经验及大型项目开发的人就知道编程规范对他们来说有多么的重要!

本文主要针对C/C++语言编程的规范给大家讲述。

文件结构

每个 C/C++程序通常分为两个文件。一个文件用于保存程序的声明,称为头文件。另一个文件用于保存程序的实现,称为定义文件,也叫实现文件。

C/C++程序的头文件以“.h”为后缀,C 程序的定义文件以“.c”为后缀,C++程序的定义文件通常以“.cpp”为后缀。

C/C++程序的头文件与实现文件都要进行版权和版本的相关声明。

版权和版本的声明 :

/*
********************************************************************************
* Copyright (C) 2015-2020, VVD Robo Co., Ltd 
********************************************************************************
* Filename      : debug.c
* Author		: Mstar
* Version       : V1.00
* Created       : 20200322
* Description 	: 常用调试接口函数
********************************************************************************
*/

头文件的作用:

(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。

(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。

头文件结构

// 版权和版本声明如上,此处省略.
#ifndef __DEBUG_H__			//防止debug.h被重复引用
#define __DEBUG_H__
/*
****************************************************************
*                               INCLUDES
****************************************************************
*/
#include <stdio.h>			//引用标准库头文件
...
#include "sys_log.h"		//引用非标准库头文件
...
void GlobalFunName(...);	//全局函数声明
...
class CBox					//类结构声明
{
	CBox(...);
	...
};

#endif //__DEBUG_H__

实现文件结构

// 版权和版本声明如上,此处省略.
#include "debug.h"
...

//全局函数实现
void GlobalFunName(...)
{
	...
}

//类成员函数的实现体
void CBox::CBox(...{
	...
}

原则:
A.头文件中只放置接口的声明,不放置实现
B.头文件应当职责单一;

规则:
A.每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接口;
B.禁止头文件循环依赖;
C.禁止包含用不到的头文件;
D.禁止在头文件中定义变量;
E.只能通过包含头文件的方式使用其他.c提供的接口

命名规则

必须遵守的规则

1、变量名、函数名都只能是字母(A-Z,a-z)、数字(0-9)或下划线。

2、第一个字母不能是数字,例如 2L 这不是一个合法的C/C++变量名、函数名。

3、不能是C/C++关键字,例如不能用char、int 、class这些单词来命名一个变量、函数。

需尽量做到的规则

1、尽量用英文,不用拼音。

2、尽量用少的单词表达多的意思,建议长度控制在10个字符以内。

3、尽量直观且可以拼读,可望文知意,不必进行“解码”。

4、尽量与所采用的操作系统或开发工具的风格保持一致。

程序命名

下位机(单片机)程序我喜欢用类似Windows程序的命名规则,linux下的程序就用linux的风格,这两者还是有较大区别。

类名

类的名称一般以大写字母“C”开头,表明定义的是类,后跟一个或多个单词。为便于界定,每个单词的首字母要大写。类的命名推荐用"名词"或"形容词+名词"的形式。例如:

class CNode; // 类名
class CLeafNode; // 类名

类的数据成员加前缀 m_(表示member),可以避免数据成员与成员函数的参数同名。
例如:

void CNode::SetValue(int nWidth, int nHeight) 
{ 
 	m_nWidth = nWidth; 
 	m_nHeight = nHeight; 
} 

函数名

函数的名称由一个或多个单词组成。为便于界定,建议采用帕斯卡(Pascal)命名法,即每个单词的首字母要大写。

void  Draw(void); // 函数名
void  SetValue(int value); // 函数名

变量和参数

用小写字母开头的单词组合而成。例如:

bool bFlag; 
int nDrawMode; 

尽量少用全局变量,如果不得已需要,则使全局变量加前缀 g_(表示 global)。例如:

int g_nHowManyPeople; // 全局变量
int g_nHowMuchMoney; // 全局变量

静态变量加前缀 s_(表示 static)。例如:

void Init() 
|  |  |
|--|--|
|  |  |

{ 
	static int s_nInitValue; // 静态变量} 

宏、常量、枚举

全用大写的字母,用下划线分割单词。例如:

#define   MIN(a, b)			(a) > (b)? (b) : (a)

const int MAX = 100; 
const int MAX_LENGTH = 100; 

enum {
	START,
	...
	END,
};

其它

为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反
映软件性质的前缀。例如三维图形标准 OpenGL 的所有库函数均以 gl 开头,所有常量(或宏定义)均以GL 开头。

表达式和语句

运算符的优先级

如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。
由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,应当用括号确定表达式的操作顺序。例如:

word = (high << 8) | low 
if ((a | b) && (a & c)) 

复合表达式

如 a = b = c = 0 这样的表达式称为复合表达式。允许复合表达式存在的理由是:

(1)书写简洁; (2)可以提高编译效率。但要防止滥用复合表达式。

不要编写太复杂的复合表达式。例如:

i = a >= b && c < d && c + f <= g + h ; // 复合表达式过于复杂

不要有多用途的复合表达式。例如:

d = (a = b + c) + r ;

该表达式既求 a 值又求 d 值。应该拆分为两个独立的语句:

a = b + c; 
d = a + r; 

if 语句使用注意

  1. 不可将布尔变量直接与1、0 进行比较。

根据布尔类型的语义,零值为“假”(记为false),任何非零值都是“真”(记为 true)。 true 的值究竟是什么并没有统一的标准。例如
Visual C++ 将 true 定义为 1,而 Visual Basic 则将 true定义为-1。

  1. 不可将浮点变量用“== ”或“!=”与任何数字比较。

    千万要留意,无论是 float 还是 double 类型的变量,都有精度限制。

所以一定要避免将浮点变量用“==” 或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。 假设浮点变量的名字为 x,应当将

	if (x == 0.0) // 隐含错误的比较,

转化为

	if ((x >= -EPSINON) && (x <= EPSINON)) 

其中 EPSINON 是允许的误差(即精度)。

  1. 应当将指针变量用“==”或“!=”与 NULL 比较

    指针变量的零值是“空”(记为 NULL)。尽管 NULL 的值与 0 相同,但是两者意义不同。

排版与格式

规则:

A.程序块采用缩进风格编写,每级缩进为4个空格;

B.相对独立的程序块之间、变量说明之后必须加空行;

C.一条语句不能过长,由关键字if, for, do, while,case,switch等引导独占一行;

D.多个短语句(包括赋值语句)不允许写在同一行内,即一行只写一条语句;

E.在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格; 进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格;

参考文献

上代码

/*
********************************************************************************
* Copyright (C) 2015-2020, VVD Robo Co., Ltd 
********************************************************************************
* Filename      : debug.h
* Author		: Mstar
* Version       : V1.00
* Created       : 20200322
* Description 	: 常用调试接口函数
********************************************************************************
*/
#ifndef __DEBUG_H__
#define __DEBUG_H__

/*
********************************************************************************
*                               INCLUDES
********************************************************************************
*/
#include "sys_log.h"
/*
********************************************************************************
*                             EXTERN VALUE
********************************************************************************
*/

/*
********************************************************************************
*                               DEFINES
********************************************************************************
*/
#define DBG_BUF_LEN         512
#define DEBUG_ENABLE 		0

#if (DEBUG_ENABLE)
#define TRACE_DEBUG(FORMAT,...) 
#else
#define TRACE_DEBUG				printf
#endif

#ifndef APP_DEBUG
#define APP_DEBUG   			TRACE_DEBUG
#endif 
/*
__FILE__用以指示本行语句所在源文件的文件名; 
__LINE__用以指示本行语句在源文件中的位置信息
__func__用以指示所在的函数(VC下不可用);都是大小写敏感,GCC下可用*/
#define FUNC_T() 		{APP_DEBUG("%s\r\n", __func__);}
#define FUNC_I() 		{APP_DEBUG("%s.ENTER\r\n", __func__);}
#define FUNC_O() 		{APP_DEBUG("%s.EXIT\r\n", __func__);}
#define FUNC_ENTRY() 	{APP_DEBUG("%s[%d]:%s.ENTRY\n", __FILE__, __LINE__, __func__);}
#define FUNC_EXIT()		{APP_DEBUG("%s[%d]:%s.EXIT\n\n", __FILE__, __LINE__, __func__);}
#define FUNC_ERROR()	{APP_DEBUG("%s[%d]:%s.ERROR\n\n", __FILE__, __LINE__, __func__);}
#define FUNC_TRACE() 	{APP_DEBUG("%s()\n", __func__); }
/*
********************************************************************************
*                                 ENUM
********************************************************************************
*/
typedef enum {
	DB_DAT = 0, 	//数据
	DB_STR = 1,		//字符串
    COMM_DB_MAX,
}DB_TYPE;//打印类型
/*
********************************************************************************
*                                STRUCT
********************************************************************************
*/
typedef struct
{
    char *cmd;  //监听命令
    void *call; //万能回调
}DB_CMD_FUN;	//监听类函数

/*
********************************************************************************
*                               FUNCTIONS
********************************************************************************
*/

/*
********************************************************************************
*                                END
********************************************************************************
*/
#endif//__DEBUG_H__
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章