Click to See Complete Forum and Search --> : Linux C macro


kErNeL kRaCkEr
04-10-2001, 04:06 PM
I stumbled accross a fairly interesting C macro in the Linux kernel source
that has stumped me. Obviously the macro works, but the syntax/logic is
somewhat hard to swallow. A little enlightenment would be appreciated.

/*
* Macro used to acces an element in a list.
* Type casting a '0' to type* then accessing its member....?
*/

#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

/*
* Example of its use
*/

struct super_block {
...
struct list_head s_files;
...
} *sb = &some_super_block;

struct file {
...
struct list_head f_list;
...
} *file;

struct list_head *p;

for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
struct file *file = list_entry(p, struct file, f_list);
do something to 'file'
}

Danny Kalev
04-10-2001, 04:20 PM
kErNeL kRaCkEr wrote:
>
> I stumbled accross a fairly interesting C macro in the Linux kernel source
> that has stumped me. Obviously the macro works, but the syntax/logic is
> somewhat hard to swallow. A little enlightenment would be appreciated.
>
> /*
> * Macro used to acces an element in a list.
> * Type casting a '0' to type* then accessing its member....?

this is a common trick for calculating the offset of a member in a
struct. The offsetof() macro works this way too.
> */
>
> #define list_entry(ptr, type, member) \
> ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
you cheat the compiler, telling it to treat a NULL pointer as a pointer
to the struct. Then you try to access a member of through that pointer
and store its address. Because the we used NULL, the result is the
number of bytes between the struct's beginning and the member's address.
This whole story is expressed in this expression: &((type *)0)->member))
Now since we got the offset of a specific member in a struct, we need to
subtract it from the enclosing struct to get that enclosing struct's
address: (char *)(ptr)-(unsigned long). Finally, we cast the result to
the desired type.

Danny

>
> /*
> * Example of its use
> */
>
> struct super_block {
> ...
> struct list_head s_files;
> ...
> } *sb = &some_super_block;
>
> struct file {
> ...
> struct list_head f_list;
> ...
> } *file;
>
> struct list_head *p;
>
> for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
> struct file *file = list_entry(p, struct file, f_list);
> do something to 'file'
> }