001
23.05.2009, 13:01 Uhr
0xdeadbeef
Gott (Operator)
|
Das ganze sieht mir der sog. "Linux Kernel List" recht ähnlich, eine etwas wilde (aber sehr flexible) C-Konstruktion. Das Konzept ist, dass ein Member vom Typ list_head eine Struktur zu einem Listenknoten macht. Weil der list_head-Member in jeder Instanz der Struktur an der selben Stelle liegt, kann man aus dem Wissen, wo er liegt, die umgebende Struktur rekonstruieren. Das sieht etwa so aus:
C++: |
struct list_head { struct list_head *prev, *next; };
struct data_node { struct list_head ls; int data; };
/* ... */
struct list_head *ptr; struct data_node *data_ptr;
/* Nehmen wir an, ptr zeigt auf eine gültige Node */ data_ptr = (struct data_node *)((char *)(ptr) - (ptrdiff_t)(&((struct data_node *)NULL)->ls));
|
Dabei ist
C++: |
(ptrdiff_t)(&((struct data_node *)NULL)->ls)
|
der Offset des ls-members in einem struct data_node, der vom bekannten Zeiger auf den ls-Member eines struct data_node abgezogen werden muss, um die Adresse des data_node-Structs zu erhalten.
Natürlich wird der ganze Kram am Ende durch Makros verdeckt wie folgt:
C++: |
#define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
so dass sich das ganze im Code nachher so liest:
C++: |
data_ptr = list_entry(ptr, struct data_node, ls);
|
Es ist eine etwas dreckige Geschichte, aber in C ist man das ja gewohnt. Komm nur nicht auf die Idee, so was in C++ machen zu wollen! -- Einfachheit ist Voraussetzung für Zuverlässigkeit. -- Edsger Wybe Dijkstra |