最近發現兩個非常牛的宏:
一個是計算某個結構體成員的偏移量,
一個是計算一個結構體的首地址
這兩個宏在Linux kernel裏非常基礎,非常常用,
今天抽閒暇時間好好調試了一番。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member)*__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
其中,typeof的使用類似於sizeof的使用,
typeof返回一個數據類型,sizeof返回一個數據結構的大小(byte),
////gcc version
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
1、宏offsetof的例子(來自man offsetof)
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
struct s {
int i;
char c;
double d;
char a[];
};
/* Output is compiler dependent */
printf("offsets: i=%ld; c=%ld; d=%ld a=%ld\n",
(long) offsetof(struct s, i),
(long) offsetof(struct s, c),
(long) offsetof(struct s, d),
(long) offsetof(struct s, a));
printf("sizeof(struct s)=%ld\n", (long) sizeof(struct s));
exit(EXIT_SUCCESS);
}
//output
offsets: i=0; c=4; d=8 a=16
sizeof(struct s)=16
【分析】
- 該宏中字段TYPE爲結構體類型,MEMBER爲結構體內的變量名。
- (TYPE *)0) 是欺騙編譯器說有一個指向結構TYPE 的指針,其地址值0 。
- (TYPE *)0)->MEMBER 是獲得結構體TYPE中成員變量MEMBER的地址. 因爲基址爲0,所以,這時MEMBER的地址當然就是MEMBER在TYPE中的偏移了。
- 同時也考慮了字節對齊。
2、宏container_of的例子
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
int main(void)
{
struct s {
int i;
char c;
double d;
char a[];
};
struct s m_s, *p_m_s;
double *p_d = &m_s.d;
printf("0x%.8x\n",&m_s);
printf(" 0x%.8x \n 0x%.8x \n 0x%.8x \n 0x%.8x \n",&m_s.i,&m_s.c,&m_s.d,m_s.a);
//p_m_s = container_of(p_d, struct s, d);
printf("--0x%x\n",container_of(p_d, struct s, d));
exit(EXIT_SUCCESS);
}
////output
0xb39e8290
0xb39e8290
0xb39e8294
0xb39e8298
0xb39e82a0
--0xb39e8290
其中有三個地址是一樣的,也就是結構體的首地址。
【分析】
- typeof( ( (type *)0)->member )爲取出member成員的變量類型,
- 定義__mptr指針ptr爲指向該成員變量的指針(即指向ptr所指向的變量處),
- (char *)__mptr - offsetof(type,member)), 用該成員變量的實際地址減去該變量在結構體中的偏移,來求出結構體起始地址,
- 在這個例子裏如果不添加宏container_of,有可能編譯不過,原因:或許typeof是庫自帶,或許是我的編譯路徑不全,或許其它^_^。
另外,最近老在想sizeof到底是如果實現的?