關於宏:container_of和 offsetof以及list_for_each_entry
1.offsetof(TYPE, MEMBER)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof是一個自定義的宏,其返回值是一個member成員在一個type類型的結構體中相對於結構體首地址的字節偏移量;
分析其工作原理:
1.(TYPE *)0將0地址強制轉換成TYPE *類型指針---------且可以認為,這個類型的結構體的首地址是0x0;
2.(TYPE *)0->MEMBER以指針形式訪問成員MEMBER;
3.&((TYPE *)0->MEMBER)取成員MEMBER的地址;
因為是從0地址開始的,所以取得成員變量MEMBER的地址等於=相對與結構體首地址的偏移地址;
4.((size_t) &((TYPE *)0)->MEMBER),然後將強制類型轉換成int型,作為offsetof的返回值;
2.container_of
#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) * __mptr = (ptr);(type *)( (char *)__mptr - offsetof(type,member) );})
作用:根據一個結構體變量中的一個域成員變量的指針(也就是地址)來獲取指向整個結構體變量的指針(地址)的功能
分析:
1).const typeof( ((type *)0)->member ) * __mptr = (ptr)
typeof( ((type *)0)->member )這裏獲得member成員的類型;
typeof()就是由變量名得到變量數據類型的;
2).const typeof( ((type *)0)->member ) * __mptr = (ptr)
定義一個const typeof() * __mptr類型的指針,並初始化為ptr;這裏的ptr是一個指向type類型結構體成員變量member的指針,也就是member在結構體中的地址;
3).(char *)__mptr
這裏將__mptr指針強制類型轉換成char *類型:
原因是: 如果_mptr為int *類型, _mptr - offset 相當於減去 sizeof(int)*offset個字節;
4).offsetof(type,member)
就是一個得到member相當於結構體首地址的偏移量,以字節單位;
5).(char *)__mptr - offsetof(type,member)
得到_mptr所在結構體的首地址;
6).(type *)( (char *)__mptr - offsetof(type,member))
將得到的結構體首地址強制類型轉換成type *類型;
3.list_for_each_entry
#define list_for_each_entry(pos, head, member) for (pos = list_entry((head)->next, typeof(*pos), member); prefetch(pos->member.next), &pos->member != (head); pos = list_entry(pos->member.next, typeof(*pos), member))
在這裏,list_for_each_entry就是一個for循環:
for ( pos = list_entry((head)->next, typeof(*pos), member); prefetch(pos->member.next), &pos->member != (head); pos = list_entry(pos->member.next, typeof(*pos), member))
1) pos = list_entry((head)->next, typeof(*pos), member)
其中:
#define list_entry(ptr, type, member) container_of(ptr, type, member)
因此:
pos = list_entry((head)->next, typeof(*pos), member) 等價於: pos = container_of((head)->next, typeof(*pos), member)
所以:pos被初始化為一個指向 結構體成員變量member所在的結構體(結構體類型為typeof(*pos)) 的指針;
由於container_of的第一個參數是head->next,所以被初始化的pos是指向頭結點的下一個節點;
2) prefetch(pos->member.next), &pos->member != (head)
判斷是否又到達頭結點,表明遍歷結束
3) pos = list_entry(pos->member.next, typeof(*pos), member)
是pos指向下一個鏈表節點;
關於宏:container_of和 offsetof以及list_for_each_entry