|
工作中经常遇到有人问下面两个有趣的宏定义:
一、
在linux内核里的list_head结构指针转换成宿主结构指针的问题:其实这个东
西就是得到寄主的地址!在linux内核中,需要做多种结构体的链表或树(如进程
,文件)的插入、删除等操作,这样,每一种结构你都需要设计一种insert(void
,void......),del(void,void......)等函数来操作这些队列,为了设计一
类通用的函数解决所有这些结构的插入,删除等操作,特定义了
struct list_head
{
struct list_head *next;
struct list_head *prev;
}
结构体,并且嵌入到你要拉链的结构体中,但是在应用中我们要访问的是寄主结构
,现在我们能直接得到的是这个嵌入的机构体的地址,为了得到寄主的地址,就得
减去list_head相对寄主地址的偏移量,于是系统设计人设计了
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
来返回寄主的地址(其中ptr是list_head的首地址,type是寄主结构的类型,
member是list_head申明的成员名)。
&((type *)0) 这个0代表我定义这个寄主结构体在地址值为0的地方,也就是说,
我的这个结构体的首地址为0。这样&((type *)0)->member将返回member成员的地
址相对寄主首地址的偏移量,用member成员的地址减去这个偏移量,就得到了寄主
结构的首地址。
有的人或许对这个(unsigned long)(&((type *)0)->member)有疑问,感觉很奇怪
。其实char指针本来就是一个32位(32位平台)的long型的量,&((type *)0)-
>member的值也是!(unsigned long)(&((type *)0)->member)这样做当然可以,
就像这样:
(unsigned long)0x2000和(unsigned long *)0x2000一样,难道我们对于平时
常用的把数值强制转换为指针见怪不怪,现在把地址(指针)强制转换为数值就见
怪就怪了吗?其实系统设计师只是利用编译器的编译特性,"欺骗"编译器来达到自
己的目的,也就是一个技巧罢了。
二、
在有的应用中需要用到2的n次方字节对齐的数组(其中一个目的就是为了实现
cache缓存线对齐,得到非常高的数据访问效率),例如在PC上的图像处理的程序
中,你会发现有这样的定义:
对齐数组宏定义!
#define DECLARE_ALIGN_ARRAY(type,name,size,alignment) \
type name##AlignArray[size+alignment];\
type *name = (type *)((unsigned long)(name##AlignArray+
(alignment-1))& ~((unsigned long)(alignment)-1))
这样应用
DECLARE_ALIGN_ARRAY(int,test,500,32);
注意,只能在函数里面(局部数组)这样定义这样的数组宏,要想定义全局的对齐
的数组宏,捎该动即可!
我们知道,PC的高速缓存就是32字节对齐的,对于一个小宏块的DCT变换,可以在
函数中定义一个这样的数组,把内存里面的数据拷贝到这个数组,第一次访问这个
数组的时候就把数据缓存在一条高速线里面的数据处理都是在这个缓存线里面读数
据,效率非常的高。