En explorant les sources du noyau Linux je suis tombé face à un cast qui me laisse perplexe:oops:
C'est le 0 que je ne comprend pas!!Code:
1
2 typeof( (( type *)0->member) *__mptr
Si quelqu'un pouvait éclairer ma lanterne ce serait sympa, merci.
Version imprimable
En explorant les sources du noyau Linux je suis tombé face à un cast qui me laisse perplexe:oops:
C'est le 0 que je ne comprend pas!!Code:
1
2 typeof( (( type *)0->member) *__mptr
Si quelqu'un pouvait éclairer ma lanterne ce serait sympa, merci.
Et tu as compris tout le reste du noyau Linux ?
Blagues à part.
Etant donné que ton expression (qui n'est pas correctement parenthèsée) est utilisée dans un typeof, ça peut être un moyen très artificiel de récupérer le type du champ membre... en gros, il y a de fortes chances pour que la valeur 0 ne soit jamais déréférencée.
Qu'espère-tu trouver dans les sources de GNU/Linux ? GNU/Linux n'est pas écrit en C standard, mais en GNUC.Citation:
Envoyé par Vigneau
Par exemple, typeof n'est pas du C standard. C'est du GNUC.Citation:
Code:
1
2 typeof( (( type *)0->member) *__mptr
C'est juste une valeur particulière et portable pour un pointeur.Citation:
C'est le 0 que je ne comprend pas!!
Si quelqu'un pouvait éclairer ma lanterne ce serait sympa, merci.
Il y avait un article intéressant dans le magazine GNU Linux magazine/France n°86 du mois de septembre dernier au sujet des listes chaînées dans le noyau linux qui sont codées d'une manière particulière.
L'auteur explique le code que tu a posté de la manière suivante:
Soit un type définit de la manière suivante:Code:typeof( (( type *)0->member) *__mptr
Avec la notation (type *)0, c'est comme si on avait un pointeur qui pointait sur une structure appelée type qui serait située à l'adresse 0x00000000 en mémoire.Code:
1
2
3
4
5 typedef struct _type type; struct _type { int x; int y; };
(type *)0->y par exemple correspondrait maintenant au membre y situé à l'adresse mémoire corespondant à l'offset de y par rapport au début de la structure.
L'opérateur typeof est connu de gcc (comme l'a dit Emmanuel, c'est du GNUC) et te renvoit le type de y (ici, y est de type int). Ainsi,
déclare un pointeur sur le type de y, donc un pointeur sur int.Code:typeof( ((type *)0)->y ) *_mptr;
A quoi cela peut-il bien servir? Linux définit par exemple une liste doublement chaînée de la manière suivante:
Maintenant, la subtilité de ce modèle, c'est que ce n'est pas la liste qui contient les données, mais c'est plutôt les données qui embarquent la structure list_head dans leur propre structure:Code:
1
2
3 struct list_head { struct list_head *next, *prev; }
Ainsi, lorsqu'on parcours une liste chaînée, on se balade d'un membre liste_Type à l'autre. Seulement, le problème est de savoir comment récupérer la structure Type, et c'est là que ton code intervient. La macro suivante est définie dans include/linux/stddef.h:Code:
1
2
3
4
5 struct Type { int x; int y; struct list_head list_Type; };
et te renvoie la valeur de l'offset de member depuis le début de la structure. PuisCode:#define offsetof(type, member) ((size_t) &(type *)0)->member)
container_of te permet de retrouver l'adresse de la structure qui contient le membre sur lequel pointe ptr.Code:
1
2
3
4
5 #define container_of(ptr, type, member) ({ \ /* _mptr est un pointeur sur le membre initialisé par ptr */ const typeof( ((type *)0->member ) *_mptr=(ptr); \ /* on soustrait l'offset du membre à sont adresse pour obtenir l'adresse de la structure qui le contient et on cast pour avoir un pointeur sur la structure en question*/ (type *) ( (char *)_mptr - offsetof(type, member) );})
Voilà, c'est tout ce que je peux dire. J'espère que ça t'aidera un peu.
Salutations
Thierry
Ca dépend un peu comment on voit les choses : la liste chaînée existe bien, et ce ne sont pas les données qui contiennent la liste, mais plutôt les déclarations des pointeurs qui sont retardées et englobées dans une seule et même structure... ça n'empêche que pour récupérer les données, le code est bien krados comparé aux autres fichiers du noyau que j'ai pu voir !
Je trouve que c'est tout de même hors propos aborder ce genre de problèmes dans un mag comme celui-là... surtout quand ce style de C est très particulier et que les codes en question n'intéressent personne !
faudrais pas se mettre a generaliser son opinion a tout le monde ... :roll:Citation:
Envoyé par InOCamlWeTrust
C'est peut-être un peu abrupt comme formulation, mais je connais peu de gens réellement intéressés par les entrailles d'un noyau Linux... et je ne pense pas qu'il y aie vraiment beaucoup de gens customisant leur noyau en allant fouiller dans les sources... en tous cas, il y a des chances pour que le public visé par le mag ne soit pas réellement celui qui s'adonne à ce genre de customisation.
Et en ce qui concerne mon intérêt pour ce genre de code, il existe bien, avec un seul problème : la documentation des algorithmes utilisés.
Je suis assez curieux, donc ce type d'article m'intéresse, ne serait-ce que pour voir le problème sous un angle auquel je n'avais pas pensé. Je suis encore très débutant en programmation, et je n'ai pas d'a priori. J'ai lu l'article avec attention. Maintenant, je n'ai aucune idée du pourquoi de l'implantation choisie, par rapport à une implantation plus accadémique dont j'ai l'habitude.Citation:
Envoyé par InOCamlWeTrust
Un chose est certaine, c'est que je ne trouve pas type de code très lisible! Enfin, bon... le sujet n'est pas de savoir si l'article en question a bien sa place ou non dans un tel magazine. La question portait sur la clarification d'un code un peu obscure.
Bien à vous
Thierry
merci Thierry pour ces éclaircissements:D
C'est bien ça le problème : ce genre de code concerne essentiellement les gens qui connaissent le noyau et surtout le C gcc en long, en large et en travers, et je crois sincèrement que, pour un débutant, mettre le nez dans un tel programme, qui est, soit dit en passant, extrêmement dépendant du dialecte (le C gcc, qui n'est absolument pas standard) et du système, peut semer la confusion plus qu'autre chose.Citation:
Envoyé par mujigka
Par contre, je suis d'accord : l'algorithmique qui est derrière le code peut, le cas échéant, être intéresante... mais son implantation, du fait de sa très faible portabilité, ne présente qu'un intérêt limité.
C'est mon avis : on a bien-sûr le droit de ne pas être d'accord !
En l'occurance, l'opérateur typeof() est la seule spécificité GCC dans le code présenté dans ce post, et je ne crois pas qu'il faille être devin pour avoir une idée de ce qu'il fait. Mais, tu as raison, ce type de dialecte nuit à la portabilité, encore que GCC soit lui-même porté sur un nombre impressionnant de plateformes.
Thierry
Euh, c'est quand même très bizarre comme opérateur. On a du mal à voir ce qu'il peu retourner, vu qu'un type n'est pas une valeur...Citation:
Envoyé par mujigka
Tu as raison, je ne sais pas si on peut appeler ça un opérateur, car il n'a pas une valeur de retour clair. Appelons ça un mot clé GCC. typeof() semble se comporter sémantiquement un comme un type déclaré avec typedef.
Je conçois que son interprétation n'est pas nécessairement naturelle. Personnelement, je n'utilise pas ce type d'instructions dans mon code, et loin de moi l'idée de proner leur utilisation.Code:
1
2
3
4
5
6
7 typedef struct { int x; int y; } Point_s Point_s a = {10, 100}; typeof(a) b = {20, 200}; /* équivalent à Point_s b = ... mais NON-PORTABLE !!!*/
Autant pour moi. Meilleures salutations
Thierry
Je pense que c'est principalement utilisé dans des macros, histoire de combler l'absence des templates C++...
Etant donné que typeof retourne un nom de type, on peut l'employer partout où l'on peut employer un nom de type ; le K&R2 y faisait déjà allusion, si je me souviens bien, donc ça doit être une extension courante du C, mais non incluse dans la norme.
HUm.... évitons les trolls... difficile, ils sont gros...
Disons que le linux mag est écrit par ses lecteurs (si si, en partie au moins) et que les articles viennent de tous les horizons.
Il en faut pour tout le monde
Il me semble aussi, même si le terme "combler" est subjectif.Citation:
Envoyé par Médinoc
Cela permet, (entre autres du moins) d'obtenir ce que le C++ appelle les Templates.
Et donc, le sujet de ma réponse:
L'utilisation de "container_of" et de cette implémentation des listes chainées dans le noyau est simple: Eviter que chaque driver réinvente la liste chaînée pour sa petite structure, avec tous les éventuels problèmes afférents.
Notez que "container_of" est défini dans "include/linux/kernel.h" il est donc bien défini dans un cadre très particulier, le noyau linux, et donc destiné à être compilé avec les outils GNU
Vala.