C: Qu’est-ce que cette macro signifie?

Comment lisez-vous la deuxième ligne de cette macro? Que signifie (type *) 0 dans ce contexte?

#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 

Vous trouvez le type de ((type *)0)->member . Ce n’est pas vraiment déréférencer le pointeur (ce serait de la folie, je vous le dis. Folie! )

C’est une bizarrerie de C. Peut-être que cela aurait plus de sens s’ils écrivaient typeof(type.member) mais malheureusement ce n’est pas permis.

Une version légèrement plus lisible:

 #define container_of(ptr, type, member) ( \ { \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) ); \ } \ ) 

En bref, la macro définit un “test” utilisable pouvant être inséré dans des instructions if et autres. Le ((type *) 0) convertit une référence NULL dans le type en question, puis prend le sous-composant ‘membre’ du type. La macro typeof () retournera le type associé au membre “object”. Donc, il crée une variable constante __mptr du même type que le sous-composant type.member. Par exemple, si nous avions:

 typedef struct foo_s { int bogus; int bar; } foo; 

Alors si invoqué comme ceci:

 foo blah; /* and initialize it of course */ int *myptr = &foo.bar; foo *result = container_of(myptr, foo, bar); 

Ensuite, la première ligne de la macro deviendra la suivante:

 const int *__mptr = (myptr); 

La deuxième ligne de la macro calcule ensuite la position de la mémoire de la structure d’origine et renvoie le pointeur de la mémoire de la structure et la convertit correctement dans cette structure.

 (foo *)( (char *)__mptr - offsetof(foo, bar)); 

Le résultat est que ceci:

 foo *result = container_of(myptr, foo, bar); 

vous permet de prendre l’élément myptr dans la structure et d’en extraire le pointeur vers le conteneur d’origine.

Cela n’est pas utile dans l’exemple ci-dessus, car vous avez déjà access à la structure qui le contient. Mais prétendez que vous ne l’avez pas fait, car l’API dans laquelle vous vous trouvez n’a pas été transmise. Cette macro est un moyen délicat d’obtenir le conteneur parent alors qu’il n’aurait normalement pas été disponible.

La meilleure chose à faire, bien sûr, est de construire une meilleure API où ce piratage n’est pas nécessaire. Mais c’est utile si vous c

(type *)0 est essentiellement NULL . La deuxième ligne prend l’adresse de la valeur que vous lui avez donnée et soustrait ses positions dans la structure. qui vous donne l’adresse de la structure

Cela ressemble à la façon dont offsetof (type, membre) est défini.

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

(TYPE *) 0 renvoie 0 à un pointeur pointant sur 0 de type TYPE, et ((TYPE *) 0) -> membre pointe sur le membre de TYPE.

En réalité, la deuxième ligne est redondante et ne concerne que la vérification de type.

 #define container_of(ptr, type, member) ({\ (type *)( (char *)ptr - offsetof(type,member) );}) 

aurait la même sémantique, sauf pour la capacité de vérification de type.

Regardez: Justification de la macro container_of dans linux / list.h

Voir aussi l’explication générale: http://www.kroah.com/log/linux/container_of.html