由于是一个GCC扩展,所以同样上述代码在标准C中无法编译通过。
2.4 再看源码,一目了然 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})现在再看上述代码,是否已经豁然开朗?
#include <stdio.h> #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #endif #define container_of(ptr, type, member) ({ \ const typeof( ((type*)0)->member ) *__mptr = (ptr); \ (type*)( (char*)__mptr - offsetof(type,member) ); }) struct Student { int age; float score; char* name; }; int main(int argc, char** argv) { struct Student s; printf("the address of s is: %p\n", &s); struct Student *pStu = container_of(&s.score, struct Student, score); printf("the address of s is calculated as: %p\n", pStu); return 0; }也许你还存在一个疑问,({})中的第一个语句有啥用?
const typeof( ((type *)0)->member ) *__mptr = (ptr);的确,这个语句并不影响最后的计算结果。其作用是为了检查ptr是否是member对应的类型指针。假如程序员不小心把代码写成了如下:
struct Student *pStu = container_of(&s.age, struct Student, score);那么container_of宏的第一个语句就被解析出:
const float * __mptr = &s.age;由于age是int类型,而把一个int*赋值给float*变量是不合法的,所以编译器会发出警告,以提示程序员是否写错了代码。当然这种保护的作用有限,假如age类型也为float,则无法被编译器发现。不论如何,这种不会带来计算负担的检查还是值得的。
3 标准C如何实现学习Linux内核源码的一个好处就是把其中的一些编程技巧应用到平时的项目中。然而Linux内核之外的世界里很少使用这么多的GCC扩展,那么是否我们可以用标准C来实现类似功能的container_of呢?
把typeof(),({}),扩展去掉后,我们就得到了一个使用标准C编写的简化版本的container_of。
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) (type*)( (char*)(ptr) - offsetof(type, member) )由于是兼容标准C,所以在GCC,VC++等主流编译器下均可以顺利编译通过。