container_of


参照先:http://blog.csdn.net/yinkaizhong/article/details/4093795 http://www.cnblogs.com/sdphome/archive/2011/09/14/2176624.html container_ofの関数実装:
#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)); })

offsetofについてはstddef.hを参照してください.
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPEはあるstructのタイプ0である仮想TYPEのタイプstructであり、MEMBERはそのstructのメンバーである.このstructのベースアドレスは0であるため、MEMBERのアドレスはそのメンバーのstructヘッダアドレスに対するオフセット量である.
typeofについては、変数タイプを宣言するためのgccのC言語拡張予約語である.
const typeof((type*)0->member)*_mptr=(ptr);memberと同じタイプのポインタ定数*_mptrを宣言し、ptrに初期化することを意味する.
(type*)((char*)_mptr-offsetof(type,member);このstructにおけるmemberのオフセット量を差し引いたアドレスを_mptrのアドレスからtype型ポインタに変換することを意味する.このポインタがmemberのエントリアドレスである.
次の図に示します.
container_of_第1张图片
構造体struct student構造体を定義し、stuを初期化する.container_ofの役割は、stuのメンバーのアドレスを知り、stuという構造体のアドレスを算出することである.stu->sexのアドレスを知っていれば、stuのアドレスを返す
ステップ3で完了:
(1)構造体メンバーを得る 物理アドレス  得られる場合:sut->sex
const typeof(((type *)0)->member)*__mptr = (ptr);
(2)構造体におけるメンバのオフセット量を求め、図のようにして得る sturct studentでは、メンバーsexの オフセット20
offsetof(type,member)
(3)stuを得る 物理アドレス ,上図のように sut = stu->sex - 20  ,これでstu物理アドレスを返すことができます. 
(type *)( (char *)__mptr - offsetof(type,member) )
次に、インスタンス・プログラムを示します.
#include<stdio.h>
struct student{
        char name[20];
        char sex;
        int num;
}stu= {"mingming",'m',101};
main()
{
        struct student *stu_ptr;
        int offset;
        const typeof(((struct student*)0)->sex) *_mptr = &stu.sex;//(1)    _mptr= stu->sex     
                printf("_mptr= 0x%d
",_mptr); offset = (int)(&((struct student *)0)->sex);//(2) printf("offset = %d
",offset); stu_ptr = (struct student *)((char*)_mptr - offset);//(3) stu printf("stu_ptr = 0x%d
",stu_ptr); printf("stu_ptr->name:%s\tstu_ptr->sex:%c
", stu_ptr->name, stu_ptr->sex); return 0; }

container_ofは、上記の手順の3つのステップに分解されて完了します.
(1)メンバ_mptr=stu->sexの物理アドレスを取得する
まず_mptrポインタを定義します.タイプはstruct student構造体のsexメンバーのタイプです.typeofは取得((((struct student*)0)->sex)のタイプです.ここではchar、(struct student*)0)offsetofで説明します.
(2)構造体におけるメンバーの絶対オフセット
((struct student*)0)は、0アドレスを強制的にstudent構造体タイプを指すポインタに変換するものであり、このポインタはアドレス0から21バイトでnameとsex(char name〔20〕とchar sexの21バイト)を格納し、sexは20バイト目(0バイトから)に格納され、&((struct student*)0)->sexはsexアドレス(ここでは20)を取り出す強制的に整形に変換するのでoffsetは20で、後のprintfの結果はこの点を証明します.
(3)構造体stuの物理アドレスを得る
((char*)_mptr-offset)ここでまず_mptrポインタを文字形ポインタに変換します.(なぜですか._mptrが整形ポインタ_mptr-offsetでsizeof(int)*offsetバイトを減算することに相当します.)offset値を減算すると、_mptrが存在する構造体のヘッダアドレス(stuのアドレス)が得られます.そして、このアドレスをstruct studentタイプに強制的に変換すれば正常に使用できます.
上記のプログラムの実行結果は次のとおりです.
root@android-virtual-machine:/uniteq_smb/test# ./teset_conta 
_mptr= 0x134520872
offset = 20
stu_ptr = 0x134520852
stu_ptr->name:mingming	stu_ptr->sex:m