最近阅读了Code reading 《代码阅读方法与实践》看了前几章,后边是些高级应用,留待以后研究。
1)#define new(type) (type *) calloc(sizeof(type), 1)
[...]
node = new(struct codeword_entry);
学学宏定义的多巧妙
2)
| In modern C and C++ programs you can often identify arguments passed by reference for efficiency purposes because they are identified by a const declarator.[6] [6] netbsdsrc/bin/stty/print.c:56 |
static char *ccval (const struct cchar *, int);
为了提高效率,同时为了保证参数不被修改,用const 加传址调用。
3)
char *inet_ntoa(struct in_addr ad)
{
unsigned long int s_ad;
int a, b, c, d;
static char addr[20];
s_ad = ad.s_addr;
d = s_ad % 256;
s_ad /= 256;
c = s_ad % 256;
s_ad /= 256;
b = s_ad % 256;
a = s_ad / 256;
sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
return addr;
}
在函数内部定义成static变量,函数返回这个地址后,此地址不会被释放,所以不会丢失,但是程序必须保证在下次调用这个函数前必须把addr中的数据移走,否则其中的内容将会被改变。
4)函数指针传递
void
getfile(void (*fill)(char *, long), (*skip)(char *, long))
{
[...]
(*fill)((char *)buf, (long)(size > TP_BSIZE ?
fssize : (curblk - 1) * TP_BSIZE + size));
[...]
(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
TP_BSIZE : size));
[...]
(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
[...]
}
函数指针作为函数参数是个比较高级而且灵活的应用。根据传入函数指针的不同实现对文件不同的操作。
5)问题:位段结构
| In certain cases a bit field may be declared to specify a precise range of bits used to hold a particular value on the given device.[32] [32] netbsdsrc/sys/dev/pci/if_fxpreg.h:116–125 |
struct fxp_cb_config {
[...]
volatile u_int8_t byte_count:6,
:2;
volatile u_int8_t rx_fifo_limit:4,
tx_fifo_limit:3,
:1;
struct RPR_ATD_TLV_HEADER |
位段结构是一种特殊的结构, 在需按位访问一个字节或字的多个位时, 位结构比按位运算符更加方便。
位结构定义的一般形式为:
struct位结构名{ |
其中: 整型常数必须是非负的整数, 范围是0~15, 表示二进制位的个数, 即表示有多少位。
变量名是选择项, 可以不命名, 这样规定是为了排列需要。
例如: 下面定义了一个位结构。
struct{ |
位结构成员的访问与结构成员的访问相同。
例如: 访问上例位结构中的bgcolor成员可写成:
ch.bgcolor |
位结构成员可以与其它结构成员一起使用。 按位访问与设置,方便&节省
例如:
struct info{ |
上例的结构定义了关于一个工从的信息。其中有两个位结构成员, 每个位结构成员只有一位, 因此只占一个字节但保存了两个信息, 该字节中第一位表示工人的状态, 第二位表示工资是否已发放。由此可见使用位结构可以节省存贮空间。
注意不要超过值限制
6)编写面向对象c程序
struct domain {
int dom_family; /* AF_xxx */
char *dom_name;
void (*dom_init)(void); /* initialize domain data structures */
[...]
int (*dom_rtattach)(void **, int);
/* initialize routing table */
int dom_rtoffset; /* an arg to rtattach, in bits */
int dom_maxrtkey; /* for routing layer */
}
用函数指针模拟方法,但是这些结构都需要初始化。编写代码也想写面向对象程序一样调用,理解起来很符合人的思想。
for (dom = domains; dom; dom = dom->dom_next)
if (dom->dom_family == i && dom->dom_rtattach) {
dom->dom_rtattach((void **)&nep->ne_rtable[i],
dom->dom_rtoffset);
break;
struct file {
[...]
short f_type; /* descriptor type */
short f_count; /* reference count */
short f_msgcount; /* references from message queue */
struct ucred *f_cred; /* credentials associated with descriptor */
struct fileops {
int (*fo_read)(struct file *fp, struct uio *uio,
struct ucred *cred);
int (*fo_write)(struct file *fp, struct uio *uio,
struct ucred *cred);
int (*fo_ioctl)(struct file *fp, u_long com,
caddr_t data, struct proc *p);
int (*fo_poll)(struct file *fp, int events,
struct proc *p);
int (*fo_close)(struct file *fp, struct proc *p);
} *f_ops;
off_t f_offset;
caddr_t f_data; /* vnode or socket */
};
用fileops指针实现函数共享方法。不同实体可以指向不同的fileops操作。
7)union
union overhead {
union overhead *ov_next; /* when free */
struct {
u_char ovu_magic; /* magic number */
u_char ovu_index; /* bucket # */
#ifdef RCHECK
u_short ovu_rmagic; /* range magic number */
u_long ovu_size; /* actual block size */
#endif
} ovu;
#define ov_magic ovu.ovu_magic
#define ov_index ovu.ovu_index
#define ov_rmagic ovu.ovu_rmagic
#define ov_size ovu.ovu_size
};
定义联合是需要两个值不可能同时发生,这样可以节省空间。注意到宏定义,这样方法可以方便的直接访问联合中结构的成员,不用反复写结构名了。如:
op->ov_index = bucket;
8)但更多的时候union不是为了节省空间,而是为了用一个对象存储多种不同的数据类型,一般都是在union中用enum表示不同类型。
enum msg_type {
CALL=0,
REPLY=1
};
[...]
struct rpc_msg {
u_int32_t rm_xid;
enum msg_type rm_direction;
union {
struct call_body RM_cmb;
struct reply_body RM_rmb;
}ru;
};
9)
union record {
char charptr[RECORDSIZE];
struct header {
char name[NAMSIZ];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char linkflag;
char linkname[NAMSIZ];
char magic[8];
char uname[TUNMLEN];
char gname[TGNMLEN];
char devmajor[8];
char devminor[8];
} header;
};
这个方法我应该熟悉,这是利用联合代表便宜,访问同地址数据,但是使用时需要注意,最好以字节为单位,因为如果以long,或者int为单位,有可能高地位颠倒,这是我花了好长时间自己发现的。J
下面是一个访问整数内部位置的应用。
double
frexp(double value, int *eptr) <-- a
{
union {
double v; <-- b
<-- c
struct {
u_int u_mant2 : 32; <-- d
u_int u_mant1 : 20;
u_int u_exp : 11; <-- e
u_int u_sign : 1;
} s;
} u;
if (value) {
u.v = value; <-- f
*eptr = u.s.u_exp - 1022; <-- g
u.s.u_exp = 1022; <-- h
return(u.v); <-- i
} else {
*eptr = 0;
return((double)0);
}
}
10)typedef
typedef char ut_line_t[UT_LINESIZE];
However, decrypting such declarations is not too difficult. Consider typedef to be a storage class specifier like extern or static, and read the declaration as a variable definition.
static char ut_line_t[UT_LINESIZE];
The name of the variable being defined (ut_line_t in the case above) is the type's name; the variable's type is the type corresponding to that name.
用这个方法就会破解teypdef复杂的类型定义了:
把typedef想象成static,然后变量的名字就是类型的名,变量所对应的类型就是typedef定义的类型。
11)
rknots = xalloc(MAXORD * numKnots * sizeof(rknots[0][0]));. |
ddFLOAT (*rknots)[MAXORD]=0; /* reciprocal of knot diff */
PclFontMapRec ** index; <-- a
[...]
index = (PclFontMapRec **)xalloc(sizeof(PclFontMapRec *)*nindex_row); <-- b
[...]
for (i=0; i<nindex_row; i++) { <-- c
index[i] = (PclFontMapRec *)xalloc(sizeof(PclFontMapRec)*nindex_col);
[...]
for (j=0; j<=nindex_col; j++)
index[i][j].fid = 0x0; <-- d
}
对于二维数组的访问可以定义个宏,方便通用。
#define DATA( n, i, j ) ( float * ) ( *data + ( i * n ) ) + j
12)MAP
static struct key {
char *name; <-- a
void (*f)(struct info *); <-- b
[...]
} keys[] = {
{ "all", f_all, 0 },
{ "cbreak", f_cbreak, F_OFFOK },
[...]
{ "tty", f_tty, 0 },
};
[...]
int
ksearch(char ***argvp, struct info *ip)
{
char *name;
struct key *kp, tmp;
name = **argvp;
[...]
tmp.name = name;
if (!(kp = (struct key *)bsearch(&tmp, keys, <-- d
sizeof(keys)/sizeof(struct key), sizeof(struct key), c_key)))
return (0);
[...]
kp->f(ip); <-- e
return (1);
}
这种映射表的方法应用在了lmhub中telnet功能上,实现命令和函数相对应。V4的cgi函数映射表也是这种方法。
13)SET
pbitvec[j/BITS_PER_LONG] |= ((unsigned long)1 << (j % BITS_PER_LONG));
上面是把j放入一个集合pbitvec中。先是整除,然后是取余,并进行标记。
用&可以判断j是否在一个集合中:
#define FD_ISSET(n, p) /
((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
从集合中去除一个元素:
#define FD_CLR(n, p) /
((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
对于这个集合的理解很肤浅,留待以后遇到时在学习。