1. 变量
大部分结构体变量和局部变量都要加上前缀,后面是首字母大写的单词组合,大部分前缀具有通用意义,也有部分前缀是专用的。基本上在SQLite代码里大部分都是带前缀的变量。
1.1通用前缀变量
具有计算意义的整型变量要加前缀i,表示长度的变量加前缀sz,表示数量的加前缀n,指针变量加前缀p,数组或一片连续的地址空间要加a,判读是否的变量要加is,指针数组要加ap,字符串指针前要加z,二级指针要加pp,布尔变量前加b等等,前缀z有时也有来代替前缀a,状态变量用前缀e,下面是一些例子:
int iHash
int iLimit
u16 szPage;
sqlite3_int64 iOffset
u32 nBackfill;
int nSegment;
int nByte
sqlite3_vfs *pVfs;
sqlite3_file *pDbFd;
PgFreeslot *pNext;
u32 *aOut
u8 *aData
void *pBuf
u8 isInit;
volatile u32 **apWiData;
int bUnderPressure;
char *zBulk;
const char *zWalName;
u8 eState;
int eMode
u32 **ppPage
u32 *pnTruncate,
1.2自定义前缀变量
下面是一些专用的前缀的例子,根据需要自行定义,前缀小写,后面单词首字母大写:
u8 bigEndCksum;
u32 mxFrame;
int savedMaxPage;
i16 readLock;
int createFlag
u8 syncFlags;
u8 exclusiveMode;
u8 readOnly;
u8 truncateOnCommit;
u32 minFrame;
i64 journalOff;
int badHdr;
1.3不带前缀的变量
在局部变量中,有些不好定义前缀的变量,或者已经约定俗成了的,可以用短小的小写字母单词表示:
u32 unused;
u32 pgno
int flags;
int fd;
volatile u32 *page0;
只有一个单词,且字母不超过5个的整型变量可以把前缀去掉,但容易搞混淆的变量最好保留前缀,比如iKey可以改为key,而iHash改为hash就不妥,因为还有aHash变量,加上前缀可以做一个区分。
结构体定义的变量一般用小写字母表示,如
WalIndexHdr hdr;
PcacheGlobal pcache1;
PGroup grp;
WalIndexHdr h1, h2;
1.4单字母变量
在一些特定的计算模块内可以使用单字母局部变量,如i表示循环索引,n表示循环个数,s表示数量的总和,sz表示长度,h表示hash表索引,cnt表示计数器,rc表示返回值等等
1.5特例
在函数中的局部指针主要是写的简短,来替换传入的指针参数,如
PgFreeslot *p;
PgHdr1 *pX
PgHdr1 **pp;
有一些变量的指针不以p开头,主要是因为这些变量太简短了,而且能表示明确的意义
sqlite3 *db
sqlite3_file *fd;
sqlite3_mutex *mutex;
总之,在SQLite源代码的变量命名总的规则是在意思表达明确的前提下尽量做到简短。
2.函数名
大部分函数名都是前缀加首字母大写的单词组合在一起,前缀小写,这部分函数都一般都是在模块内部使用或只为上一层提供,主要是这些函数构成了SQLite的主体功能,耦合性较高,大部分前缀代表特定的模块名,也有一些前缀表示执行特定的功能:
walMergesor
sqlite3PagerClose
sqlite3BtreeOpen
pcache1FetchStage2
demoWrite
syncJournal
sqlite3VdbeMemSetPointer
getPageNormal
pageInJournal
有些函数使用小写字母的单词用下滑线连接,首字母为前缀,使用下滑线的一般都是公共的独立对外接口,也有一些非对外接口的函数也使用下划线,这部分函数的耦合性较低,可能有些也仅仅只是个人风格:
btree_open
sqlite3_open_v2
sqlite3_create_function
pager_write_pagelist
pager_open_journal
pager_end_transaction
函数指针统一用x作为前缀,如:
int (*xBusyHandler)(void*);
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
3.类型名
无符号变量用u表示,有符号变量用i表示,后面跟的数字表示变量的位数,像一些比较简短的类型如char、int就没必要再重新定义了
typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned short u16;
typedef short i16;
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
有时候为了更明确地表达意思,有些类型重新定义成特殊用途的
typedef u16 ht_slot;
typedef long _off_t;
typedef u32 Pgno;
4.宏定义
宏定义一般都使用多个大写字母的单词用下划线连接在一起:
WALINDEX_MAX_VERSION
WAL_ALL_BUT_WRITE
PAGER_WRITER_DBMOD
PAGER_OPEN
MAX_SECTOR_SIZE
5.结构体
结构体名第一个单词的字母大写,结构体名中没有前缀,即使有前缀第一个字母也要大写:
typedef struct WalIndexHdr WalIndexHdr;
typedef struct Pager Pager;
typedef struct PgHdr DbPage;
typedef struct PCache1 PCache1;
部分结构体使用小写字母加下滑线的组合,这些结构体定义的对象一般用来耦合外部模块比较多:
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_file sqlite3_file;
typedef struct sqlite3_pcache_page sqlite3_pcache_page;
typedef struct sqlite3_mutex sqlite3_mutex;
typedef struct sqlite3_api_routines sqlite3_api_routines;
typedef struct sqlite3_context sqlite3_context;
6.常用格式
结构体先在.c文件里定义好,在要用的地方加上声明,如.c文件里有一个结构体:
struct Bitvec {}
然后在需要的头文件里添加下面这一行头文件的声明:
typedef struct Bitvec Bitvec- 函数参数一般不超过3个,超过后书写格式如下
int sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, int eTextRep, void *p, void (*xSFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ){
缩进为2个空格
if( nHeader>JOURNAL_HDR_SZ(pPager) ){ nHeader = JOURNAL_HDR_SZ(pPager); }
空2格主要是为了看起来更紧凑吧,目前主流还是空4格。
一行写不下时有多种格式,有换行空一定格数的,也有与第一个参数对齐的,看情况选用
assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize ); rc = sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE; if( rc==SQLITE_OK && pPager->dbHintSize<pPager->dbSize && (pList->pDirty || pList->pgno>pPager->dbHintSize) ) memset(&zHeader[sizeof(aJournalMagic)+20], 0, nHeader-(sizeof(aJournalMagic)+20));
- 花括号不换行
但是个人写代码更喜欢换行,因为换行后更方便调试,比如
换行后,如果调试时想要去掉if条件,只需把if注释掉即可,而且换行后花括号对齐,而且结构性较强if() { }
//if() { }
- 参数传入的结构体变量不要直接拿来运算,先用一个简短的局部变量定义一下,这样代码就会看起来简洁清晰许多,如
u8 eOld = pPager->journalMode; Pager *pPager = pPg->pPager;
- 空格
在=、||、&运算符两边要加空格
一般情况下为了看起来紧凑点==、>=、加减乘除等运算符不要加空格pList = pList->pDirty;
offset%JOURNAL_HDR_SZ(pPager)==0 sizeof(aJournalMagic)+4
但是有时候计算元素较多时也需要空格,这是为了让一些局部的组合运算看起来更加整体一些
*pOffset += pPager->pageSize + 4 + isMainJrnl*4; (*pOffset <= pPager->journalHdr)
if、assert、while后面的圆括号要加空格,for、函数调用和运算中的圆括号不要加空格,但是for和函数调用的参数之间要加空格
newSize = szPage*(i64)nPage; if( (currentSize+szPage)<=newSize ) sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); for(u=0; u<nRec; u++) assert( pPager->pWal ); while( rc==SQLITE_OK && pList )
布尔变量用!判断,其他一般用==0判断
if( rc==SQLITE_OK && !pPager->noSync ) if( !isOpen(pPager->jfd) ) if( doTruncate || iLimit==0 ){ if( pPager->aSavepoint[ii].iHdrOffset==0 )
- 特别常用的结构体变量,将其宏定义
#define MEMDB pPager->memDb #define isOpen(pFd) ((pFd)->pMethods!=0)
创建一个独立模块时通常格式如下
把这个模块要用到的数据定义成一个结构体,如Demo,然后为其创建一个对象,分配所需空间,初始相关化数据,并返回一个连接句柄:Demo *pDemo = demoOpen();
然后再创建该模块用到的一系列方法,调用时只要传入连接句柄即可
void demoXXX(Demo *pDemo)
不再使用时,释放分配的空间,销毁对象:
void demoClose (Demo *pDemo)
在代码量较大,逻辑关系耦合性强时,尽量在容易出错的地方加上assert,这样一旦出错就会在前期发现,而不会把问题留到后期。