container_of
container_of est une macro très utile et définie dans le kernel linux (./include/linux/kernel.h), qui permet de récupérer l’adresse d’une structure à partir d’un de ses membres:
/*! * container_of - cast a member of a descriptor out to the containing descriptor * * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
Elle prend en paramètres (comme décrit plus haut):
- ptr: le pointeur que nous manipulons, membre de la structure instanciée dont nous voulons récupérer l’adresse
- type: le type de la structure qui contient ce membre,
- member: le nom du membre dans la déclaration de la structure.
La premiere ligne de la macro permet de déclarer un pointeur qui va contenir l’adresse du membre.
Ce pointeur va être correctement déclaré grâce au cast réalisé par:
typeof( ((type *)0)->member )*
(type *) 0 permet de déclarer un pointeur de structure de type (type) à l’adresse 0. On récupère alors le membre qui nous intéresse et plus précisément son type avec typeof, opérateur unaire comme sizeof défini par gcc (http://gcc.gnu.org/onlinedocs/gcc/Typeof.html).
La deuxième ligne peut se décomposer en deux parties:
- offsetof(type, member) va calculer le décalage du champ member à partir du début de la structure type, en octet. offsetof (3) est une macro définie dans stddef.h,
- on soustrait à l’adresse contenue par __mptr l’offset calculé par offsetof. On caste __mptr en (char *) afin que la soustraction soit effectuée en octet (sinon le compilateur fixera comme unité la taille du membre member).
L’adresse retournée est donc l’adresse de la structure contenant le membre ptr.
Prenons un exemple:
struct toto { char a; char b; };
À un instant t dans mon code, je manipule un pointeur sur le membre b d’une structure toto instanciée préalablement en mémoire: (char *)ptr_sur_b;
Je n’ai plus dans le contexte de la fonction l’adresse de cette structure et pour une raison quelconque (par exemple accéder au membre a) je veux récupérer cette adresse:
struct toto *t; /* je récupère l'adresse de la structure grâce à containerof */ t = containerof(ptr_sur_b, struct toto, b); /* je peux alors accéder au membre a de la structure t: t->a */
Si l’on remplace member et type dans la macro par ce qui nous intéresse ici:
ligne 1:
const typeof( ((type *)0)->member ) *__mptr = (ptr); => const typeof ( ((struct toto*)0)->b ) *__mptr; => const char *__mptr;
ligne 2:
(struct toto *) ( (char *)__mptr - offsetof (struct toto, b)); => (struct toto *) ( (char *) __mptr - 1 );
On retrouvera entre autre l’utilisation de cette macro dans l’implémentation des listes (./include/linux/list.h) du kernel.
