Abstruse #define macro rencontrée dans la source du kernel Linux

Le get marcro get_cpu_var qui est défini comme suit

29 #define get_cpu_var(var) (*({ \ 30 extern int simple_identifier_##var(void); \ 31 preempt_disable(); \ 32 &__get_cpu_var(var); })) 

Cela me semble incompréhensible. Je suppose qu’il s’agissait d’un type de macro de fonction renvoyant un pointeur de variable (basé sur l’astérisque) ou d’un type de pointeur de fonction.Je suis même proche? Quelqu’un pourrait-il m’éclairer?

Ce que vous voyez entre l’ouverture ({ et la fermeture }) est une expression d’instruction – une fonctionnalité non standard du compilateur GCC, qui permet d’incorporer des instructions composées dans des expressions C. Le résultat d’une telle déclaration expression est la toute dernière déclaration d’expression à l’intérieur du ({}) . Dans votre cas, ce serait &__get_cpu_var(var) .

L’opérateur & est appliqué au résultat de la sous-expression __get_cpu_var(var) . Cela implique que __get_cpu_var retourne une lvalue. S’il s’agit bien de C, alors __get_cpu_var doit également être une macro, car les fonctions en langage C ne peuvent pas renvoyer de valeurs.

L’opérateur & produit un pointeur (le résultat de l’expression entière de l’instruction), qui est ensuite déréférencé par un opérateur * présent au tout début de la définition de macro ci-dessus. Ainsi, la macro ci-dessus est essentiellement équivalente à l’expression *&__get_cpu_var(var) .

Certains pourraient demander pourquoi il est implémenté en tant que *&__get_cpu_var(var) et pas simplement __get_cpu_var(var) . Ceci est fait de cette façon pour préserver la valeur du résultat de __get_cpu_var(var) . Le résultat de l’instruction statement est toujours une valeur, même si la dernière étape à l’intérieur de ({}) était une valeur. Afin de préserver la qualité du résultat, nous utilisons le fameux *& sortingck.

Cette astuce n’est en aucun cas limitée aux expressions d’instruction GCC. Il est relativement souvent utilisé dans la programmation en langage C ordinaire. Par exemple, imaginons que vous ayez deux variables

 int a, b; 

et vous voulez écrire une expression qui renverrait a ou b tant que lvalue (disons que nous voulons lui affecter 42 ) en fonction de la variable de select . Une tentative naïve pourrait ressembler à ceci

 (select ? a : b) = 42; 

Cela ne fonctionnera pas, car en langage C, l’opérateur?: Perd la valeur de ses opérandes. Le résultat est une valeur qui ne peut pas être affectée. Dans cette situation, l’astuce *& astuce vient à la rescousse

 *(select ? &a : &b) = 42; 

et maintenant cela fonctionne comme prévu.

C’est exactement comment et pourquoi la définition de macro de l’affiche originale contient une application apparemment redondante de * et & . Pour cette raison, vous pouvez utiliser la macro get_cpu_var ci-dessus de part et d’autre d’une requête.

 something = get_cpu_var(something); get_cpu_var(something) = something; 

sans cette astuce, vous ne get_cpu_var utiliser get_cpu_var du côté droit.

En langage C ++, le même effet est obtenu en utilisant des références . En C, nous n’avons aucune référence, nous utilisons donc des astuces comme celle-ci.