Donné par un collègue comme un casse-tête, je ne peux pas comprendre comment ce programme C se compile et s'exécute réellement. Qu'est-ce que cet >>>=
opérateur et l'étrange 1P1
littéral? J'ai testé à Clang et GCC. Il n'y a aucun avertissement et la sortie est "???"
#include <stdio.h>
int main()
{
int a[2]={ 10, 1 };
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
printf("?");
return 0;
}
0x.1P1
est un littéral hexadécimal avec un exposant. C'est0x.1
la partie numérique, ou 1/16 ici. Le nombre après le «P» est la puissance de deux, le nombre est multiplié par.0x.1p1
Est donc vraiment 1/16 * 2, ou 1/8. Et si vous vous posiez la question,0xFULL
c'est juste0xF
, etULL
c'est le suffixe pour ununsigned long long
Réponses:
La ligne:
contient les digraphes
:>
et<:
, qui se traduisent par]
et[
respectivement, c'est donc équivalent à:Le littéral
0xFULL
est le même que0xF
(qui est hex pour15
); leULL
spécifie juste que c'est ununsigned long long
littéral . Dans tous les cas, en tant que booléen, c'est vrai, donc0xFULL ? '\0' : -1
évalue à'\0'
, qui est un littéral de caractère dont la valeur numérique est simplement0
.Pendant ce temps,
0X.1P1
est un littéral à virgule flottante hexadécimal égal à 2/16 = 0,125. Dans tous les cas, étant différent de zéro, c'est également vrai en tant que booléen, donc le nier deux fois avec!!
produit à nouveau1
. Ainsi, le tout se résume à:L'opérateur
>>=
est une affectation composée qui décale son opérande gauche vers la droite du nombre de bits donné par l'opérande de droite et renvoie le résultat. Dans ce cas, l'opérande de droitea[1]
a toujours la valeur1
, il est donc équivalent à:ou équivalent:
La valeur initiale de
a[0]
est 10. Après avoir décalé une fois vers la droite, il devient 5, puis (arrondi vers le bas) 2, puis 1 et enfin 0, point auquel la boucle se termine. Ainsi, le corps de la boucle est exécuté trois fois.la source
P
dans0X.1P1
.e
dans10e5
, sauf que vous devez utiliserp
des littéraux hexadécimaux care
c'est un chiffre hexadécimal.p
sépare la mantisse et l'exposant, tout comme lae
notation flottante scientifique normale; une différence est que, avec des flotteurs hexadécimaux, la base de la partie exponentielle est 2 au lieu de 10, donc0x0.1p1
égale à 0x0.1 = 1/16 fois 2¹ = 2. (Dans tous les cas, rien de tout cela n'a d'importance ici; tout non différent de zéro la valeur fonctionnerait aussi bien là-bas.)char
littéral", et j'ai ajouté un lien Wikipedia. Merci!Il s'agit d'un code assez obscur impliquant des digraphes , à savoir
<:
et:>
qui sont des jetons alternatifs pour[
et]
respectivement. Il y a aussi une certaine utilisation de l' opérateur conditionnel . Il y a aussi un opérateur de décalage de bits , l'affectation de décalage à droite>>=
.Ceci est une version plus lisible:
et une version encore plus lisible, remplaçant les expressions dans le
[]
pour les valeurs qu'elles résolvent:Remplacer
a[0]
eta[1]
pour leurs valeurs devrait permettre de comprendre facilement ce que fait la boucle, c'est-à-dire l'équivalent de:qui effectue simplement une division (entière) par 2 à chaque itération, produisant la séquence
5, 2, 1
.la source
????
, cependant, plutôt que???
comme l'OP l'a obtenu? (Huh.) Codepad.org/nDkxGUNi fait des produits???
.Passons en revue l'expression de gauche à droite:
La première chose que je remarque, c'est que nous utilisons l'opérateur ternaire depuis l'utilisation de
?
. Donc, la sous-expression:dit "si
0xFULL
est différent de zéro, retour'\0'
, sinon-1
.0xFULL
est un littéral hexadécimal avec le suffixe long-long non signé - ce qui signifie que c'est un littéral hexadécimal de typeunsigned long long
. Cela n'a pas vraiment d'importance, car0xF
peut tenir dans un entier normal.De plus, l'opérateur ternaire convertit les types des deuxième et troisième termes en leur type commun.
'\0'
est ensuite converti enint
, qui est juste0
.La valeur de
0xF
est bien supérieure à zéro, elle passe donc. L'expression devient maintenant:Ensuite,
:>
est un digraphe . C'est une construction qui se développe pour]
:>>=
est l'opérateur de décalage à droite signé, nous pouvons espacer celaa
pour le rendre plus clair.De plus,
<:
est un digraphe qui se développe pour[
:0X.1P1
est un littéral hexadécimal avec un exposant. Mais peu importe la valeur, la valeur!!
de tout ce qui n'est pas nul est vraie.0X.1P1
est0.125
qui est non nul, il devient donc:Le
>>=
est l'opérateur de décalage vers la droite signé. Il modifie la valeur de son opérande gauche en décalant ses bits vers l'avant de la valeur sur le côté droit de l'opérateur.10
en binaire est1010
. Voici donc les étapes:>>=
renvoie le résultat de son fonctionnement, de sorte que tant que le décalagea[0]
reste non nul à chaque fois que ses bits sont décalés de un vers la droite, la boucle continue. La quatrième tentative est oùa[0]
devient0
, donc la boucle n'est jamais entrée.Par conséquent,
?
est imprimé trois fois.la source
:>
est un digraphe , pas un trigraphe. Il n'est pas géré par le préprocesseur, il est simplement reconnu comme un équivalent de jeton]
.?:
) a un type qui est le type commun des deuxième et troisième termes. Le premier terme est toujours conditionnel et a un typebool
. Puisque les deuxièmes et troisièmes termes ont le typeint
le résultat de l'opération ternaire seraint
, nonunsigned long long
.#
et##
avoir des formes de digraphe; rien n'empêche une implémentation de traduire des digraphes en non-digraphes pendant les premières phases de la traduction