Purpose of offsetof and container_of

Two macro definitions, offsetof() and container_of(), are common in the Linux kernel. We may also need to use it in application programming, so we can copy these two macro definitions.

offsetof(type, member) is used to find the offset of the member member in the structure type in type, which is defined as follows:

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

For example for struct:

struct test_s {
    int a;
    int b;
    int c;
};

Then offsetof(struct test_s, c) is equal to 8.

The container_of(ptr, type, member) macro is used to obtain a pointer to the entire structure through a pointer to a member of the structure.

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

where type is the type of the structure, member is the name of the member in the structure, and ptr is a pointer to an instance of this member. For example for struct:

struct camdev {
    int a;
    int c;
    int b;
};

Then the result of the following code is to point the pointer zzp to the structure zz.

struct camdev zz; //一个结构体实例
struct camdev * zzp;
zzp = container_of(&(zz.c), struct camdev, c);

Of course, this code is just for example. The actual scenario may be that we don't know where zz is defined and its address, but we know that c is a member of this structure and we know the pointer of c, then we can use container_of to get the instance of zz.

-
Two points need to be explained:
1. The content after the expansion of the offsetof() macro is processed in the compilation stage (the second stage of the compilation process), not at the runtime, so using the 0 address to fetch a member will not cause an exception.
2. container_of() and offsetof() are macros provided in the kernel. We cannot use them directly when writing applications, but redefine them.

As a casual remark: Computable member offsets can be obtained at compile time, for example I saw a program like this:

#include <stdio.h>

struct str{
    int len;
    char s[0];
};

struct foo {
    struct str *a;
};

int main(int argc, char** argv) {
    struct foo f={0};
    if (f.a->s) {
        printf("%x\n", f.a->s);
    }
    return 0;
}

This program does not report an error when executed, and the print result is 4. Because although f = {0} points a to NULL, printf in the code prints the address of fa->s, which can be calculated during compilation. If you change %x to %s the program crashes because you are trying to access the content pointed to by s.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324878026&siteId=291194637