宏CONTAINING_RECORD的用处其实还是相当大的, 而且很是方便, 它的主要作用是:
根据结构体中的某成员的地址来推算出该结构体整体的地址! 下面从一个简单的例子开始说起: 我们定义一个结构体, 同时类型化:typedef struct{ int a; int b; int c;}ss;
这是一个很简单的结构体, 没什么特殊的, 稍微分析下该结构体(假设在32位平台上):
结构体的大小(字节):4+4+4=12字节 成员a的偏移:0 成员b的偏移:4 成员c的偏移:8 我们用ss来定义一个变量: ss s = {1,2,3}; 那么此时a,b,c的值分别为:a=1,b=2,c=3.其实编译器在生成代码的时候其实是这样给成员变量赋值的: 假定s的地址为:0x12000000, 则: *(int*)((char*)&s + 0) = 1; *(int*)((char*)&s + 4) = 2; *(int*)((char*)&s + 8) = 3; 也就是说是在&s的地址基础上加上变量的偏移来确定成员的指针并赋值的, 所以: &s->a 将得到 0x12000000 + 0 = 0x12000000 &s->b 将得到 0x12000000 + 4 = 0x12000004 &s->c 将得到 0x12000000 + 8 = 0x12000008 所以: 结构体的地址 + 成员变量的偏移 = 成员变量的地址 移一下项: 成员变量的地址 - 成员变量的偏移 = 结构体的地址 哇哇, 这就是我们想要的地址, 不就是做了个减法嘛~~~囧 首先, 成员变量的地址是我们知道的. 其次, 我们需要得到成员变量的偏移(假定为成员b的偏移). 怎么办呢? 我们可以这样: &s->b - (unsigned long)&s, 这样就可以得到成员b的偏移了, 但是, 但是, &s 是我们需要的, 显然暂时是个未知数, 既然这样... 那, 我们再做一次减法吧(非正确的C语言表达式, 不过结果没问题, 这里只是显得清楚点): &(s-s)->b - (unsinged long)&(s-s), 其中, 为保证类型一致, 需要: s-s = (ss*)0 (unsigned long)&(s-s) = (unsigned long)(ss*)0 = 0, 直接省略该部分就可以了 那么, 化简得到: &((ss*)0)->b - (unsigned long)0 最简结果: &((ss*)0)->b, 这就是b的偏移 哈哈, 很简单吧, 0指针的妙用, 总共做了两次减法而已~ 对你们数学帝来说肯定不是问题啦~ 其中, 我们需要知道ss结构体的原型, ss结构体中的某个成员变量b(其实无论哪个都一样, 只是要和前面提供指针的那个变量要一致) 总结下, 我们需要提供:结构体中某个成员变量的地址, 该结构体的原型, 该结构体中的某个成员变量(与前面要是同一个变量) 最终的CONTAINING_RECORD的定义为:#define CONTAINING_RECORD(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field)) addr: 结构体中某个成员变量的地址 type: 结构体的原型 field: 结构体的某个成员(与前面相同)