container_of

note of Linux 核心原始程式碼巨集: container_of
在 linux source code 裡被 referenced 巨多次
https://elixir.bootlin.com/linux/latest/A/ident/container_of

struct 的記憶體行為

struct data {
    short a;
    char b;
    double 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; }

指標操作前須轉型為 (char*) 做加減才會 shift 1 byte.

 printf("p=%p, &x.c=%p\n", p, &(x.c));
//p=0x16dad74a3
//&x.c=0x16dad74a8

why?
data-alignment

需改成

struct data {
    short a;
    char b;
    double c;
} __attribute__((packed));;

但就會有效能疑慮
How to get correct pointer?
offsetof

image

用法:

p = (char *) &x + offsetof(struct data, c);
#define offsetof(st, m) \
    ((size_t)((char *)&((st *)0)->m - (char *)0))    
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

我爸的指標 = container_of(我的指標, 爸爸, 兒子)

用 container_of 實現物件導向

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

例子
imx214.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));    \
    })
char *parent = __extension__\
({ const __typeof__(((struct data *) 0)->c) *(__pmember)
= (p); \
(struct data *) ((char *) __pmember - offsetof(struct data, c)); });