1739 votes

Qu'est-ce que ":-!!" dans le code C?

Je suis tombé sur cet étrange code de macro dans /usr/include/linux/kernel.h:

/* Force a compilation error if condition is true, but also produce a
   result (of value 0 and type size_t), so the expression can be used
   e.g. in a structure initializer (or where-ever else comma expressions
   aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))

Qu'est - :-!! ?

1753voto

John Feminella Points 116878

C'est, en effet, une manière de vérifier si l'expression e peut être évalué à 0, et si non, l'échec de la construction.

La macro est un peu mal nommée; il devrait être quelque chose de plus comme BUILD_BUG_OR_ZERO, plutôt que d' ...ON_ZERO. (Il y a eu occasionnellement des discussions quant à savoir si c'est une confusion de nom.)

Vous devriez lire l'expression comme ceci:

sizeof(struct { int: -!!(e); }))
  1. (e): Calculer l'expression e.

  2. !!(e): Logiquement annuler deux fois: 0 si e == 0; sinon, 1.

  3. -!!(e): Numériquement nier: 0 si e == 0; sinon, -1.

  4. struct{int: -!!(0);} --> struct{int: 0;}: Si c'était de zéro, nous déclarons une structure avec un anonyme entier champ de bits qui a une largeur égale à zéro. Tout est beau et on procède comme à la normale.

  5. struct{int: -!!(1);} --> struct{int: -1;}: D'un autre côté, si elle n'est pas égal à zéro, alors qu'il faudra un certain nombre négatif. La déclaration de tout champ de bits avec négatif largeur est une erreur de compilation.

Nous allons donc soit se retrouver avec un champ de bits qui a la largeur 0 dans une struct, ce qui est bien, ou un champ de bits négatif de largeur, ce qui est une erreur de compilation. Puis nous prenons sizeof ce domaine, nous obtenons donc un size_t avec la largeur appropriée (qui sera de zéro dans le cas où l' e est égal à zéro).


Certaines personnes ont demandé: Pourquoi ne pas simplement utiliser un assert?

keithmo réponse ici a une bonne réponse:

Ces macros mettre en œuvre au moment de la compilation de test, tandis que la fonction assert() est un test.

Tout à fait exact. Vous ne voulez pas de détecter les problèmes dans votre noyau à l'exécution qui pourraient avoir été pris plus tôt! C'est une pièce essentielle du système d'exploitation. Autant de problèmes peuvent être détectés au moment de la compilation, tant mieux.

265voto

David Heffernan Points 292687

L' : est un champ de bits. Comme pour !!, ce qui est logique double négation , et donc les retours 0 pour fausse ou 1 pour vrai. Et l' - est un signe, c'est à dire l'arithmétique de la négation.

C'est tout simplement un truc pour que le compilateur barf entrées non valides.

Envisager BUILD_BUG_ON_ZERO. Lors de l' -!!(e) donne une valeur négative, ce qui produit une erreur de compilation. Sinon, -!!(e) prend la valeur 0, et 0 largeur de champ de bits a une taille de 0. Et donc la macro donne un size_t de la valeur 0.

Le nom est faible, à mon avis, parce que la génération en fait échoue lorsque l'entrée n'est pas zéro.

BUILD_BUG_ON_NULL est très similaire, mais renvoie un pointeur plutôt qu'un int.

173voto

keithmo Points 1164

Certaines personnes semblent être source de confusion ces macros avec assert().

Ces macros mettre en œuvre au moment de la compilation test, en assert() est un test.

57voto

Daniel Santos Points 766

Eh bien, je suis assez surpris de voir que les alternatives à cette syntaxe n'ont pas été mentionnés. Une autre commune (mais les plus âgés) mécanisme est d'appeler une fonction qui n'est pas défini et s'appuient sur l'optimiseur de compiler l'appel de la fonction si votre affirmation est correcte.

#define MY_COMPILETIME_ASSERT(test)              \
    do {                                         \
        extern void you_did_something_bad(void); \
        if (!(test))                             \
            you_did_something_bad(void);         \
    } while (0)

Bien que ce mécanisme fonctionne (aussi longtemps que des optimisations sont activés), il a l'inconvénient de ne pas signaler une erreur jusqu'à ce que vous liez, à laquelle il ne parvient pas à trouver la définition de la fonction you_did_something_bad(). C'est pourquoi les développeurs du noyau de départ à l'aide des figures, comme le négatif de la taille des bits largeur de champ et le négatif de la taille des tableaux (le plus tard de la qui a cessé de casser construit dans GCC 4.4).

Dans la sympathie pour le besoin de la compilation des assertions, GCC 4.3 introduit l' error de la fonction d'attribut qui permet d'étendre cette ancienne concept, mais de générer une erreur de compilation avec un message de votre choix, ne plus cryptique "négatif de la taille de la matrice de" messages d'erreur!

#define MAKE_SURE_THIS_IS_FIVE(number)                          \
    do {                                                        \
        extern void this_isnt_five(void) __attribute__((error(  \
                "I asked for five and you gave me " #number))); \
        if ((number) != 5)                                      \
            this_isnt_five();                                   \
    } while (0)

En fait, comme de Linux 3.9, nous avons maintenant une macro appelée compiletime_assert qui utilise cette fonction et la plupart des macros en bug.h ont été mis à jour en conséquence. Encore, cette macro ne peut pas être utilisé comme un initialiseur. Cependant, l'utilisation par la déclaration des expressions (un autre GCC C-extension), vous pouvez!

#define ANY_NUMBER_BUT_FIVE(number)                           \
    ({                                                        \
        typeof(number) n = (number);                          \
        extern void this_number_is_five(void) __attribute__(( \
                error("I told you not to give me a five!"))); \
        if (n == 5)                                           \
            this_number_is_five();                            \
        n;                                                    \
    })

Cette macro va évaluer le paramètre exactement une fois (dans le cas où il a des effets secondaires) et de créer une erreur de compilation qui dit "je vous ai dit de ne pas me donner un cinq!" si l'expression est évaluée à cinq ou n'est pas une constante de compilation.

Alors pourquoi ne sommes-nous pas à l'aide de ces questions, au lieu de négatif de la taille en bits des champs? Hélas, il existe actuellement de nombreuses restrictions de l'utilisation de la déclaration des expressions, y compris leur utilisation en tant que constante d'initialiseurs (pour enum constantes, bits largeur de champ, etc.) même si la déclaration de l'expression est complètement constante de son auto (c'est à dire, peut être pleinement évalués au moment de la compilation et sinon passe à l' __builtin_constant_p() test). De plus, ils ne peuvent pas être utilisées en dehors du corps d'une fonction.

Espérons-le, GCC modifiera ces lacunes bientôt et permettre constante de la déclaration des expressions pour être utilisé comme une constante, les initialiseurs. Le défi ici est la spécification de langage de définition de ce qui est juridique, une expression constante. C++11 a ajouté le constexpr de mots clés pour ce genre ou de la chose, mais rien d'équivalent n'existe en C11. Tout en C11 n'a d'obtenir statique affirmations, qui permettra de résoudre en partie ce problème, il l'habitude de résoudre tous ces problèmes. J'espère donc que gcc peut faire une constexpr fonctionnalité disponible comme une extension via-std=gnuc99 & -std=gnuc11 ou quelque chose du genre et permettre son utilisation sur la déclaration des expressions et. al.

39voto

Matt Phillips Points 4520

Il est en train de créer une taille de 0 champ de bits si la condition est fausse, mais une taille de -1 (-!!1) champ de bits si la condition est vraie ou non-zéro. Dans le premier cas, il n'y a pas d'erreur et la structure est initialisé avec un int membre. Dans ce dernier cas, il y a une erreur de compilation (et pas une telle chose comme une taille de -1 champ de bits est créé, bien sûr).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X