# container_of note of [Linux 核心原始程式碼巨集: container_of](https://hackmd.io/@sysprog/linux-macro-containerof#container_of-巨集作為資料封裝的基礎) 在 linux source code 裡被 referenced 巨多次 https://elixir.bootlin.com/linux/latest/A/ident/container_of ## struct 的記憶體行為 ```c struct data { short a; char b; double c; }; ``` ```c= #include <stdio.h> struct data { short a; char b; double c; }; int main() { struct data x = {.a = 25, .b = 'A', .c = 12.45}; char *p = (char *) &x; printf("a=%d\n", *((short *) p)); //a=25 p += sizeof(short); printf("b=%c\n", *((char *) p));//b=A p += sizeof(char); printf("c=%lf\n", *((double *) p));//c=c=190346578175708908992881173523240944319242933565425065476600793027569625765132922491647361749301545251588213454633589801374830370131288956923386722390475742252870531685634044870495567872.000000. return 0; } ``` :::info 指標操作前須轉型為 (char*) 做加減才會 shift 1 byte. ::: ```c printf("p=%p, &x.c=%p\n", p, &(x.c)); //p=0x16dad74a3 //&x.c=0x16dad74a8 ``` why? [data-alignment](https://hackmd.io/@sysprog/c-memory#data-alignment) 需改成 ```c struct data { short a; char b; double c; } __attribute__((packed));; ``` 但就會有效能疑慮 How to get correct pointer? 用 [offsetof](https://en.wikipedia.org/wiki/Offsetof) ![image](https://hackmd.io/_uploads/ryZtXL746.png) 用法: ```c p = (char *) &x + offsetof(struct data, c); ``` :::info ```c #define offsetof(st, m) \ ((size_t)((char *)&((st *)0)->m - (char *)0)) ``` ```c offsetof(struct data, c) ``` would be (use gcc -E to expand macro) ``` ((size_t)((char *)&((struct data *)0)->c - (char *)0)); ``` ::: container_of 就是把藉由 struct 與 data 本身的關係,藉由 data 的指標,得到 struct 的指標 ![image](https://hackmd.io/_uploads/rJ5R4U_Va.png) ``` 我爸的指標 = container_of(我的指標, 爸爸, 兒子) ``` ## 用 container_of 實現物件導向 ```c typedef struct { int ref; } Object; typedef struct { Object base; /* Vehicle-specific members */ } Vehicle; typedef struct { Vehicle base; /* Car-specific members */ } Car; // Car 繼承了 Vehicle void vehicleStart(Vehicle *obj) { if (obj) printf("%x derived from %x\n", obj, obj->base); } int main(void) { Car c; vehicleStart((Vehicle *) &c); } ``` ![image](https://hackmd.io/_uploads/ByXwocYNp.png) 例子 [imx214.c](https://elixir.bootlin.com/linux/latest/source/drivers/media/i2c/imx214.c) ## 實作 ```c /* container_of() - Calculate address of object that contains address ptr * @ptr: pointer to member variable * @type: type of the structure containing ptr * @member: name of the member variable in struct @type * * Return: @type pointer of object containing ptr */ #define container_of(ptr, type, member) \ __extension__({ \ const __typeof__(((type *) 0)->member) *(__pmember) = (ptr); \ (type *) ((char *) __pmember - offsetof(type, member)); \ }) ``` ```c char *parent = __extension__\ ({ const __typeof__(((struct data *) 0)->c) *(__pmember) = (p); \ (struct data *) ((char *) __pmember - offsetof(struct data, c)); }); ```