第一条:结合枚举enum声明和宏定义来定义变量:这样可以不用去定义一个enum变量(Form Memcahced)
enum { DT_UNKNOWN = 0, # define DT_UNKNOWN DT_UNKNOWN DT_FIFO = 1, # define DT_FIFO DT_FIFO DT_CHR = 2, # define DT_CHR DT_CHR DT_DIR = 4, # define DT_DIR DT_DIR DT_BLK = 6, # define DT_BLK DT_BLK DT_REG = 8, # define DT_REG DT_REG DT_LNK = 10, # define DT_LNK DT_LNK DT_SOCK = 12, # define DT_SOCK DT_SOCK DT_WHT = 14 # define DT_WHT DT_WHT };
第二条:在定义结构体的时候,可以采用unsigned char格式,这样就避免字节填充(Form FastCGI)
typedef struct { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } FCGI_Header;
对于这个例子来说,本来requestID应该定义为int,为了防止字节填充所带来的内存浪费,采用上面的方式可以避免字节填充。即requestID=requestIDB1<<8+requestIDB2
第三条:当一个结构体包含的信息很多,当外部程序只需要内部的部分信息时,可以通过把该部分信息放在一个独立的结构体中,并把该小结构体放在大结构体的最前面,此时可以同一个首指针访问到两个结构体。这种信息的封装虽然不能保证信息安全,但是可以减少内部和外部程序之间耦合度和关联度(原先两个程序之间的关联是一个大结构,现在通过一个小的就可以了)(Form linux/ipc/)
大的结构体如下:
/* one msq_queue structure for each present queue on the system */ struct msg_queue { struct kern_ipc_perm q_perm; time_t q_stime; /* last msgsnd time */ time_t q_rtime; /* last msgrcv time */ time_t q_ctime; /* last change time */ unsigned long q_cbytes; /* current number of bytes on queue */ unsigned long q_qnum; /* number of messages in queue */ unsigned long q_qbytes; /* max number of bytes on queue */ pid_t q_lspid; /* pid of last msgsnd */ pid_t q_lrpid; /* last receive pid */ struct list_head q_messages; struct list_head q_receivers; struct list_head q_senders; };第一个小的结构体如下:
struct kern_ipc_perm { spinlock_t lock; int deleted; int id; key_t key; uid_t uid; gid_t gid; uid_t cuid; gid_t cgid; umode_t mode; unsigned long seq; void *security; };这样就可以通过一个struct kern_ipc_perm的指针把部分信息(一个队列的基本特征信息)开放给外部程序,而struct msg_queue中list_head q_messages为真正队列内容,在有些程序只需要特征信息而不需要队列内容时候,不需要知道struct msg_queue结构。假如现在知道特征信息后,需要继续知道具体的队列内容,只需要对struct kern_ipc_perm进行转换为struct msg_queue,就可以得到具体队列内容。
第四条:利用结构体中隐性字段来存储该结构体的内容字段,利用直接字段来存储特征内容(Form linux /ipc)
struct msg_msg { long m_type; int m_ts; /* message text size */ /* the actual message follows immediately */ };
在上面的例子里面,该结构体是用来存储一个消息(包括特征和具体内容),但是该结构体中只有一个消息类别和消息大小的字段,没有一个字段来存储实际消息内容,其实里面是利用了一个隐性字段来存储消息内容。struct msg_msg *p+sizeof(msg_msg)+[0,p->m_ts]之间就是具体的消息内容。
该技巧在写类库的时候比较好用。比如一个queue的队列,提供队列的构造,元素添加,元素删除等功能。在类库的眼中,我只关心队列中每一个元素的next字段,而不关心队列元素其他内容字段。因此在类库中定义一个节点元素和一个队列如下:
struct queue_node{ struct queue_node *next; }; struct queue{ struct queue_node *head; struct queue_node *tail; };在用户代码中,定义一个和struct queue_node结构体格式一样的结构体:第一个元素为next字段,后面可以添加内容字段。比如
struct myNode{ struct myNode *next; int data; };类库函数的参数为void *指针,在内部可以把该指针转化为struct queue_node类型的指针进行处理。
//类库代码 int addTail(void *node,struct queue *queue) { struct queue_node *qn=(struct queue_node *)node; //用户代码 struct myNode *node=(struct myNode *)removeHead(q);
这样在类库代码中,不需要知道用户代码中具体节点的节点内容。不知道还有没有更好方法?
第五条:利用宏定义来简化函数的编写和函数的使用,针对参数为变量,指针,指针的指针的情况 (Form php/zend.h)
具体看例子:
#define Z_REFCOUNT_PP(ppz) Z_REFCOUNT_P(*(ppz)) #define Z_REFCOUNT_P(pz) zval_refcount_p(pz) #define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z)) static zend_always_inline zend_uint zval_refcount_p(zval* pz) { return pz->refcount__gc; }