代码风格的问题,很微妙,也很有趣。因为它并不影响代码的运行和功能,但它连接着人的心灵和信仰。代码风格目的简单明确,就是增加代码的可阅读性,降低维护成本,减少心智负担。纠结的地方在于,每个人对“可阅读性”的理解和喜好不同。专注容易产生喜爱,喜爱容易滋生癖好,代码写的越多,越会形成个人风格习惯。而逻辑性,还容易在大脑中创造强迫症的倾向,所以代码风格会演变成信仰,难以动摇。
因为代码风格不喜欢,很可能就会否定,一门编程语言,一个框架,一个库,一个功能等等。
但其实,代码风格是大脑虚幻确定感的又一力证。因为,无论什么风格,喜欢的还是不喜欢的,被迫写上一段时间,写上成千上万行,不喜欢也喜欢了。大脑会潜意识的为内心的感受,寻找各种各样的解释和理由,而感受是会被环境数据所改写的,到时候又会有新的解读和视角。
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变量的值。
「习惯就好」