The (char *) casting in container_of() macro in linux kernel

c, kernel, linux

Solution

It's to get byte-level addresses for the calculation. Otherwise, pointer arithmetic would be in terms of the types being pointed at, which breaks since `offsetof` computes the offset in bytes.

Casting to `char *` is pretty common on the inside of low-level primitives like this, since sometimes you really need to think of memory as being an "array of bytes" and manipulate it at that level.

Consider this structure as an example:

struct demo {
  int foo;
  float bar;
};

now, if we did this:

struct demo test;
float *intptr = &test.bar;

we should be able to get a pointer to `test` using `container_of()`:

struct demo *owner = container_of(intptr, struct demo, bar);

this will expand to:

struct demo *owner = {(
    const typeof( ((struct demo*)0)->bar) *__mptr = (intptr);
    (struct demo*)( (char *)__mptr - offsetof(struct demo,bar) );})

So the first line declares a `float` pointer `__mptr` and copies the argument value to it.

The second line casts the member pointer to `char *`, then subtracts the offset of `bar` inside `struct demo` (which will be 4, if `sizeof (int)` is 4), thus getting "back up" to the address of the entire structure `test`.

If the cast to `char *` hadn't been there, the `4` would have been interpreted in terms of `float` (since `__mptr` is `float *`) which would obviously back up way too far (16 bytes instead of 4, assuming `float` is also 4 bytes) causing horrible breakage.

Note that the `{(` syntax wrapping the expansion of `container_of()` is a GCC extension; this won't work in standard C.

See this page for lots more on `container_of()`.

Problem

``` #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) ``` I already know that first line is for type checking, but for the second line, Why do it do a `(char *)` casting? I find nowhere explain this. And I do some experiment code myself, even without the casting, the result seems to be fine too.

Original source