A la différence de ce que l'on a appris
généralement en cours ici les listes ne pointent pas sur la structure
mais
sur le chaînon suivant.
Avantages ?
Mais alors, pour manipuler un champ de la structure, il
faudra qu'en même être capable de récuperer l'adresse de la structure
par l'intermédiaire du chaînon.
La difficulté majeur est la compréhension de containerof. Mais revenons
tout d'abord à la macro offsetof à la base de la macro containeurof.
La macro offsetof
Voici un exemple de structure avec l'adresse des différents champ est
l'offset en octet avec le début de la structure. Nous allons voir
comment calculer facilement l'écart en octet entre le début d'une
structure et un champ grace à la macro
offsetof.
remarque : Pour éviter ces décalages on utilise __attribute__
((packed)) dans la définition de la structure.
Définition de offsetof
offsetof(struct
S,champ))= &((struct S *)0)->champ
La définition n'est pas si évidente, mais retenons l'idée sous jacente.
Pour savoir l'écart entre un champ et le début de la structure, on
met
la structure
à l'adresse 0. Ainsi, l'adresse du champ donne l'écart entre le début
de la structure et l'adresse du champ. L'adresse de début de la
structure valant zéro, on l'oublie par la suite dans le calcul de
l'adresse du champ.
Dans l'exemple précédent,
offsetof(struct humain,age)=
-(struct humain *)0 + &((struct
nomprenomage *)0)->age
offsetof(struct humain,age)= -0+8 = 8 = &((struct
nomprenomage *)0)->age
On comprend alors la définition.
Containerof
Containerof permet de
renvoyer l’adresse du début de la structure connaissant l’adresse d’un
de ces membres. Par exemple ptr
= container_of(ptrhumain ->age,struct humain,age);
ptr = 0xff2.
Idée
On a remarqué que les chaînons ne pointent pas sur la structure que
l'on veut manipuler mais sur un membre (qui est d'ailleurs le chaînon
suivant). Grâce à containerof, on accède à la structure par le chaînon
et
containerof va renvoyer l'adresse de la structure. Cette adresse permet
ensuite de manipuler tous les champs de la structure.
définition
#define container_of(ptr, type, member) ({
const
typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})
La définition est en deux parties :
une déclaration
d'un pointeur __mptr de type const
typeof( ((type
*)0)->member )* et une affectation
le calcul d'une adresse : (char *)__mptr
- offsetof(type,member)
La
déclaration "dit" que
__mptr
est un pointeur (*) de type membre (member) d'une structure de
nom type que l'on force à l'adresse 0. Puis le pointeur __mptr est égal
à l'adresse de ptr.
le
calcul est une
soustraction
entre la valeur du pointeur (sur un membre connu) et le décalage du membre avec le début de la
structure. Cette valeur renvoie donc l'adresse du début de la structure.
Dans l'exemple ci-dessus : on aurrait (char *)__mptr=ffa et
offsetof(type,member)=8, le résultat de la
soustraction donne l'adresse du pointeur sur la structure.
Retour