Linux下的Container
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字節),實際上的結果是8offsetof(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)); \})
編譯直接通過,這類錯誤難以發現。
找了下博客園里有這么篇文章,對于實現細節已經講的挺詳細了。
作者:NumberSix
來源鏈接:https://www.cnblogs.com/numbersix/p/4304600.html
未經允許不得轉載:>貴州網站建設公司 » Linux下的Container