編程規範總結
概述
逝者如斯,歲月從指縫間溜走,年華在不覺中老去,彈指一揮間…
從用單片機點亮第一個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、0 進行比較。
根據布爾類型的語義,零值爲“假”(記爲false),任何非零值都是“真”(記爲 true)。 true 的值究竟是什麼並沒有統一的標準。例如
Visual C++ 將 true 定義爲 1,而 Visual Basic 則將 true定義爲-1。
-
不可將浮點變量用“== ”或“!=”與任何數字比較。
千萬要留意,無論是 float 還是 double 類型的變量,都有精度限制。
所以一定要避免將浮點變量用“==” 或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式。
或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式。 假設浮點變量的名字爲 x,應當將
if (x == 0.0) // 隱含錯誤的比較,
轉化爲
if ((x >= -EPSINON) && (x <= EPSINON))
其中 EPSINON 是允許的誤差(即精度)。
-
應當將指針變量用“==”或“!=”與 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__