web-dev-qa-db-fra.com

Qu'est-ce que l'opérateur >>> = en C?

Donnée par un collègue comme un casse-tête, je ne peux pas comprendre comment ce programme C est compilé et exécuté. Qu'est-ce que c'est >>>= opérateur et l'étrange 1P1 littéral? J'ai testé à Clang et GCC. Il n'y a pas d'avertissement et le résultat est "???"

#include <stdio.h>

int main()
{
    int a[2]={ 10, 1 };

    while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
        printf("?");

    return 0;
}
290
CustomCalc

La ligne:

while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )

contient les digraphes:> et <:, qui se traduit par ] et [ respectivement, cela équivaut donc à:

while( a[ 0xFULL?'\0':-1 ] >>= a[ !!0X.1P1 ] )

Le littéral 0xFULL est le même que 0xF (qui est un hex pour 15); le ULL spécifie simplement que c'est un unsigned long long littéral . En tout cas, en tant que booléen, c’est vrai, alors 0xFULL ? '\0' : -1 est évalué à '\0', qui est un littéral de caractère dont la valeur numérique est simplement 0.

Pendant ce temps, 0X.1P1 est un littéral hexadécimal à virgule flottante égal à 2/16 = 0,125. Dans tous les cas, étant non nul, c'est aussi vrai qu'un booléen, donc le nier deux fois avec !! produit à nouveau 1. Ainsi, le tout simplifie jusqu'à:

while( a[0] >>= a[1] )

L'opérateur >>= est un assignation composée ce bit décale son opérande de gauche du nombre de bits donné par l'opérande de droite et renvoie le résultat. Dans ce cas, l'opérande droit a[1] a toujours la valeur 1, c'est donc équivalent à:

while( a[0] >>= 1 )

ou équivalent:

while( a[0] /= 2 )

La valeur initiale de a[0] est égal à 10. Après avoir décalé une fois à droite, il devient 5, puis (arrondi au bas) 2, puis 1 et enfin 0, point auquel la boucle se termine. Ainsi, le corps de la boucle est exécuté trois fois.

463
Ilmari Karonen

C'est un code assez obscur impliquant digrammes , à savoir <: et :> qui sont des jetons alternatifs pour [ et ] respectivement. Il y a aussi une certaine utilisation de l'opérateur conditionnel . Il existe également un opérateur de décalage de bits , l'affectation du décalage à droite >>=.

Ceci est une version plus lisible:

while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )

et une version encore plus lisible, remplaçant les expressions dans le [] pour les valeurs qu’ils résolvent:

while( a[0] >>= a[1] )

Remplacement a[0] et a[1] car leurs valeurs devraient faciliter la compréhension de ce que fait la boucle, c’est-à-dire l’équivalent de:

int i = 10;
while( i >>= 1)

qui effectue simplement une division (entière) par 2 à chaque itération, produisant la séquence 5, 2, 1.

67
juanchopanza

Passons à travers l'expression de gauche à droite:

a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]

La première chose que je remarque est que nous utilisons l’opérateur ternaire à partir de ?. Donc, la sous-expression:

0xFULL ? '\0' : -1

dit "si 0xFULL est différent de zéro, retourne '\0', autrement -1. 0xFULL est un littéral hexadécimal avec le suffixe non signé long-long - signifiant qu'il s'agit d'un littéral hexadécimal de type unsigned long long. Cela n'a pas vraiment d'importance, car 0xF peut tenir dans un entier régulier.

De plus, l'opérateur ternaire convertit les types des deuxième et troisième termes en leur type commun. '\0' est ensuite converti en int, qui est simplement 0.

La valeur de 0xF est bien plus grand que zéro, donc ça passe. L'expression devient maintenant:

a[ 0 :>>>=a<:!!0X.1P1 ]

Suivant, :> est un digraphe . C'est une construction qui se développe en ]:

a[0 ]>>=a<:!!0X.1P1 ]

>>= est l'opérateur de décalage droit signé, nous pouvons l'espacer de a pour le rendre plus clair.

De plus, <: est un digraphe qui se développe en [:

a[0] >>= a[!!0X.1P1 ]

0X.1P1 est un littéral hexadécimal avec un exposant. Mais peu importe la valeur, le !! de tout ce qui est non nul est vrai. 0X.1P1 est 0.125 qui est non nul, il devient:

a[0] >>= a[true]
-> a[0] >>= a[1]

Le >>= est l'opérateur de décalage droit signé. Il change la valeur de son opérande de gauche en décalant ses bits de la valeur du côté droit de l'opérateur. 10 en binaire est 1010. Alors voici les étapes:

01010 >> 1 == 00101
00101 >> 1 == 00010
00010 >> 1 == 00001
00001 >> 1 == 00000

>>= _ renvoie le résultat de son opération, aussi longtemps que le décalage a[0] reste non nul à chaque fois que ses bits sont décalés de un, la boucle continue. La quatrième tentative est où a[0] devient 0, la boucle n'est donc jamais entrée.

Par conséquent, ? est imprimé trois fois.

41
0x499602D2