代碼風格的問題,很微妙,也很有趣。因爲它並不影響代碼的運行和功能,但它連接着人的心靈和信仰。代碼風格目的簡單明確,就是增加代碼的可閱讀性,降低維護成本,減少心智負擔。糾結的地方在於,每個人對“可閱讀性”的理解和喜好不同。專注容易產生喜愛,喜愛容易滋生癖好,代碼寫的越多,越會形成個人風格習慣。而邏輯性,還容易在大腦中創造強迫症的傾向,所以代碼風格會演變成信仰,難以動搖。
因爲代碼風格不喜歡,很可能就會否定,一門編程語言,一個框架,一個庫,一個功能等等。
但其實,代碼風格是大腦虛幻確定感的又一力證。因爲,無論什麼風格,喜歡的還是不喜歡的,被迫寫上一段時間,寫上成千上萬行,不喜歡也喜歡了。大腦會潛意識的爲內心的感受,尋找各種各樣的解釋和理由,而感受是會被環境數據所改寫的,到時候又會有新的解讀和視角。
Mojoc的代碼風格,完全不同於C和C++的常用習慣 ,而是最大限度的減少下劃線和宏的使用,沒有好壞對錯,只是個人喜好。
全局命名
使用駝峯命名規則,BigCamelCased 這是大駝峯, smallCamelCased 這是小駝峯。
變量命名
- 一般變量使用小駝峯。
int keyLength;
int valueTypeSize;
SkeletonBone* bone;
SkeletonBoneData* boneData;
Drawable* drawable;
- const 變量使用小駝峯。(因爲還是可以修改的所以和變量一樣)
static const char* ids[AudioId_Length];
static const char* saveDataFileName = "MojocSaveDataFile";
static const int bezierSize = (BEZIER_SEGMENTS + 1) * 2 - 1;
static const float subDivPre = subDivStep3 / pre5;
- bool 變量使用“is”前綴,整體小駝峯。
SLboolean isLoopEnabled;
bool isFound;
bool isRemoved;
particle->isActive
- 單例變量使用“A”前綴,整體大駝峯。(表示“一個”,從NDK學來的)
extern struct AComponent AComponent [1];
extern struct ADrawable ADrawable [1];
extern struct AParticle AParticle [1];
extern struct AApplication AApplication[1];
- 函數參數帶出返回值的變量使用“out”前綴,整體小駝峯。
void (*OnSaveData)(void** outSaveData, int* outLength);
void (*Inverse) (Matrix4* matrix4, Matrix4* outInverse);
void Init (int valueTypeSize, ArrayIntMap* outArrayIntMap);
- 函數指針變量使用大駝峯。(因爲會像函數一樣帶括號調用)
typedef struct
{
void (*OnPause) ();
void (*OnResume) ();
void (*OnDestroy)();
}
ApplicationCallbacks;
typedef float (*TweenActionValueOnGet)(void* target);
typedef void (*TweenActionValueOnSet)(void* target, float value);
typedef struct
{
TweenActionValueOnGet OnGet;
TweenActionValueOnSet OnSet;
}
TweenActionValueGetSet;
縮寫命名
- 縮寫單詞要麼全部大寫,要麼全部小寫。
typedef struct
{
}
RGB;
RGB rgb;
RGB myRGB;
RGB rgbDatas[10];
RGB myRGBData;
void SetRGB (RGB* rgb);
void RGBSet (RGB* rgb);
void SetRGBData(RGB* rgb);
Goto 標籤命名
- 標籤使用大駝峯,標籤地址使用一般變量命名。
goto ParseArrayEnd;
goto ParseObjectEnd;
goto UseVBO;
goto UseVAO;
goto *coroutine->step;
枚舉命名
- 枚舉類型使用大駝峯,枚舉變量大駝峯並用下劃線分割前綴。(前綴避免衝突)
enum
{
HeroState_Stand,
HeroState_DieOver,
};
enum
{
CollisionGroup_HeroBody = 1,
CollisionGroup_HeroAttack = 1 << 1,
};
typedef enum
{
FontTextAlignment_HorizontalLeft,
FontTextAlignment_HorizontalRight,
FontTextAlignment_VerticalTop,
FontTextAlignment_VerticalBottom,
}
FontTextAlignment;
typedef enum
{
InputTouchType_Up = 1,
InputTouchType_Down = 2,
InputTouchType_Move = 3,
InputTouchType_Cancel = 4,
}
InputTouchType;
函數命名
- 全局函數包括內聯的,使用大駝峯,並用下劃線分割前綴。(前綴避免衝突)
extern void Application_Main ();
static inline void AApplication_AppendChild(Component* child);
static inline float AGLTool_ToGLWidth (float screenWidth);
static inline float AMath_Random ();
- 局部函數包括內聯的,使用大駝峯。
static void LoadingRun (Coroutine* coroutine);
static inline float GetWorldScaleY(Drawable* drawable);
- 函數類型定義,使用大駝峯,必須有前綴。(前綴避免衝突)。
typedef float (*TweenActionValueOnGet)(void* target);
typedef void (*TweenActionValueOnSet)(void* target, float value);
typedef void (*CoroutineRun) (Coroutine* coroutine);
- 函數返回bool表示操作是否成功的,使用“Try”前綴。
void* (*TryPut) (ArrayIntMap* arrayIntMap, intptr_t key, void* valuePtr);
bool (*TryRemove)(ArrayIntMap* arrayIntMap, intptr_t key);
- 函數返回bool表示結果的,使用“Is”,“Test”,“Check”前綴。
bool (*IsContains) (ArrayIntSet* arrayIntSet, intptr_t element);
bool (*TestPolygonPoint) (Array(float)* vertexArr, float x, float y);
bool ADrawable_CheckVisible(Drawable* drawable);
- “Release” 命名代表釋放結構體成員所持有的內存空間。
- “Create” 前綴表示在堆上分配內存。
ArrayList* (*Create) (int elementTypeSize);
ArrayList* (*CreateWithSize) (int elementTypeSize, int size);
ArrayList* (*CreateWithCapacity)(int elementTypeSize, int capacity);
- “Init” 前綴表示初始化已有的內存空間。
void (*Init) (int elementTypeSize, ArrayList* outArrayList);
void (*InitWithSize) (int elementTypeSize, int size, ArrayList* outArrayList);
void (*InitWithCapacity)(int elementTypeSize, int capacity, ArrayList* outArrayList);
結構體命名
結構體和別名,包括聯合及別名,使用大駝峯
宏定義命名
- 頭文件的定義,單詞全大寫下劃線分割,“H”後綴。
#ifndef STYLE_GUIDE_H
#define STYLE_GUIDE_H
- 無參數宏,單詞大寫下劃線分割。
#define MATH_PI 3.141592653589793
#define MATH_2PI 6.283185307179586
#define MATH_PI2 1.570796326794897
- 有參數宏,大駝峯下劃線分割前綴。(前綴避免衝突)
// not aligned brackets, because macro rules limit
#define AMath_Min(x, y)
#define AStruct_GetParent2(memberPtr, structType)
#define ACoroutine_YieldFrames(waitFrames)
其它命名
文件名,資源名,文件夾名,通通使用大駝峯。
代碼格式
- 使用空格縮進,不使用tab縮進。
- 縮進使用4個空格。
- 指針類型的星號,靠近變量類型名的一邊。
int* p1;
int** p2 = &p1;
void* Function(char* str);
- 左右花括號“{}”垂直對齊。
{
// vertical alignment
}
- 參數括號“()” 如果換行了,就垂直對齊。
AMath_Max
(
animationData->duration,
AArray_Get
(
deformTimeline->frameArr,
deformTimeline->frameArr->length - 1,
float
)
);
static void ReadAnimationDeform
(
SkeletonData* skeletonData,
JsonObject* jsonDeform,
SkeletonAnimationData* animationData,
ArrayList* skeletonTimelineArr
)
{
// ...
}
- if, while, for, switch, 必須有花括號“{}”,與“()”空一格距離。
- 操作符兩邊至少空一格。
vertexX + (y - vertexY) / (preY - vertexY) * (vertexData[preIndex] - vertexX)
- case,break依次縮進。
switch (111)
{
case 0:
break;
case 1:
{
break;
}
}
- 函數上下空兩行間距。
static int a = 100;
static void Function1()
{
}
static inline void Function2()
{
}
struct A
{
}
- 不同的內容之間空兩行間距。
#include "AAA.h"
#include "BBB.h"
typedef float (*Function1)(void* target);
typedef float (*Function2)();
struct A
{
}
struct B
{
}
extern struct B B[1];
- 使用分割線,區分不同的邏輯的內容。
// this is split line
//--------------------
void Function1();
//--------------------
void Function2();
void Function3()
{
int a;
//----------------
int b;
}
- goto的標籤縮進與當前行保持一致。
static void Function()
{
goto Label:
Label1:
int a;
Label2:
int b;
Label3:
int c;
}
- 條件編譯的縮進與當前行保持一致。
typedef struct
{
Sprite sprite[1];
PhysicsBody* body;
Enemy* enemy;
ArrowHitType hitType;
#ifdef APP_DEBUG
Drawable debugDrawable[1];
#endif
}
Arrow;
void Function()
{
int a;
#ifdef APP_DEBUG
int b;
#endif
}
#ifdef APP_DEBUG
Drawable debugDrawable[1];
#endif
- 所有代碼儘量保持,垂直對齊,參看上面所有的例子。(ID Software的規則)
代碼註釋
- 函數外部使用塊註釋。
/**
* Comment struct
*/
struct A
{
/**
* Comment property
*/
int a;
/**
* Comment function
*/
void (*Function)();
}
- 函數內體使用行註釋 “//”。
- 註釋代碼塊或多行代碼,如下格式:
/*
--------------------------------------------
This is means comment few blocks of code
--------------------------------------------
*/
其它規則
參數宏,只有在inline無法滿足的情況下使用。比如,帶有默認參數的函數別名,宏特有的功能體現,泛型參數,變長參數。
不使用0和1作爲bool值判斷,非bool值使用明確的表達式判斷。const變量不能被修改,不可在頭文件定義const變量,在c文件修改const變量的值。
「習慣就好」