Linux下的Container_of

Linux下的Container_of

看内核代码的时候看到了container_of宏,写的真是挺巧妙的,直接起到了this指针的功能

#define container_of(ptr, TYPE, MEMBER)({   \
    const typeof(((TYPE *)0)->MEMBER) *_mptr = (ptr); \
    (TYPE*)((char*)_mptr - offsetof(TYPE, MEMBER));  \
})

当时主要有两个疑问:

  • 为什么要有个临时变量_mptr?直接使用ptr不就可以了?
  • 为什么要给mptr强转成char*?

写了段代码
Container_of的测试代码:

#include <stdio.h>

#define offsetof(TYPE, MEMBER)({(size_t) &(((TYPE *)0)->MEMBER);})

#define container_of(ptr, TYPE, MEMBER)({   \
    const typeof(((TYPE *)0)->MEMBER) *_mptr = (ptr); \
    (TYPE*)((char*)_mptr - offsetof(TYPE, MEMBER));  \
})

struct stA{
    char a;
    int b[3];
    double c;
};

int main (void) 
{
    struct stA A;
    double* x = &(A.c);

    printf("%p\n",&A);
    printf("%p <-- %p\n", x, container_of(x, struct stA, c));
    return 0; 
}

这份代码输出如下:
0x7fff5718ebd0
0x7fff5718ebe0 <-- 0x7fff5718ebd0

如果去掉_mptr前的(char),输出则为:
0x7fff519e6bd0
0x7fff519e6be0 <-- 0x7fff519e6b60
因为C语言指针变量的差值是根据指针变量类型来计算的,_mptr是double类型(8字节),实际上的结果是8
offsetof(TYPE, MEMBER)),这样计算地址当然会出错。

而对于

const typeof(((TYPE *)0)->MEMBER) *_mptr = (ptr);

一句的解释,参考了这篇文章的描述:
这句话主要作用是在编译时,能够让编译器检查ptr的类型和MEMBER的类型是否匹配,如果不匹配则产生相应的告警。
例如,

#include <stdio.h>

#define offsetof(TYPE, MEMBER)({(size_t) &(((TYPE *)0)->MEMBER);})

#define container_of(ptr, TYPE, MEMBER)({   \
    const typeof(((TYPE *)0)->MEMBER) *_mptr = (ptr); \
    (TYPE*)((char*)_mptr - offsetof(TYPE, MEMBER));  \
})

struct stA{
    char a;
    int b[3];
    double c;
};

int main (void) 
{
    struct stA A;
    double* x = &(A.c);

    printf("%p\n",&A);
    printf("%p <-- %p\n", x, container_of(x, struct stA, a)); //这里把成员变量名错写成a
    return 0; 
}

编译器在编译时会告警:

warning: incompatible pointer types initializing 'const typeof (((struct stA *)0)->a) *' (aka 'const char *') with an expression of type 'double *' [-Wincompatible-pointer-types]

如果改成这样:

#define container_of(ptr, TYPE, MEMBER)({   \
    (TYPE*)((char*)ptr - offsetof(TYPE, MEMBER));  \
})

编译直接通过,这类错误难以发现。

找了下博客园里有这么篇文章,对于实现细节已经讲的挺详细了。

posted @ 2015-02-28 00:38  NumberSix  阅读(757)  评论(0编辑  收藏  举报