第一条:结合枚举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类型的指针进行处理。
类库函数的对该指针进行处理以后,返回一个void *,在用户代码,可以把指针转化为我们想要的节点指针。
//类库代码
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;
}