Voici deux codes qui semblent faire la même chose, mais ce n'est pas le cas. Ces deux différences lors de l'exécution et de la comparaison de la sortie avec le traçage donnent une confusion car il semble que le premier traitement de code est un code dépendant de la machine. Veuillez lire les deux codes
Code 1: -
unsigned char c=(~0 << 3); c >>= 4; c <<= 1; printf("%d", c);
Sortie: - 254
Code 2: -
XXX
Sortie: -. 30
La sortie du code ci-dessus est différente.
Non seulement ce code (1er code) est source de confusion, mais tous les types de code impliquant un opérateur de décalage bit à bit sur une seule ligne donnent des résultats inattendus.
Le deuxième code fonctionne correctement.
Veuillez exécuter ce code sur votre ordinateur et vérifier la sortie ci-dessus
ET / OU
Expliquez pourquoi ces sorties ne sont pas les mêmes.
OU
Enfin, nous devons apprendre que nous ne devons pas appliquer plusieurs opérateurs de décalage au niveau du bit dans notre code.
Merci p >
5 Réponses :
~ 0 << 3
est toujours un bogue, aucun des deux exemples n'est correct.
0
est de type int
qui est signé. ~ 0
convertira le contenu binaire en tous les uns: 0xFF ... FF
. Conclusion: aucun des deux exemples n'a une sortie déterministe et les deux peuvent planter ou afficher des déchets.
Dans le premier cas, il y a une coercition vers char seulement après la fin du calcul tandis que dans le second cas il y a une coercition vers char après le premier (~ 0 << 3)
. p >
comme je l'ai dit dans les commentaires, il existe des conversions arithmétiques par défaut impliquées dans la coercition qui font la différence.
Je pense que vous voulez dire conversion . Quoi qu'il en soit, il n'y a pas de comportement déterministe du code, donc le raisonnement sur les conversions de type implicites n'est pas significatif.
Les conversions automatiques @Lundin sont également appelées coercitions.
Non, ils ne le sont pas.
@alinsoar: Pouvez-vous citer une section de la norme C pour étayer cela?
@Lundin, "Ce type de conversion de type peut être effectué implicitement ou explicitement. Conversion implicite, également appelée coercition" - en.wikibooks.org/wiki/Introduction_to_Programming_Languages/…
Ceci est étiqueté C qui est défini par ISO 9899. Tout le reste, y compris les wikis Internet trash, n'est pas pertinent. Une conversion peut être implicite ou explicite. Un casting est toujours une conversion explicite. Les termes cast (C11 6.5.4), conversion implicite (C11 6.3) et conversion explicite (C11 6.3) sont tous des termes formels, définis par la norme C. Le mot «coercition» n'existe même pas dans le document standard C.
Ok, alors maintenant j'ai cherché tous les livres C que j'ai au format pdf et je ne les ai trouvés nulle part, sauf pour un endroit: dans "Traps & Pitfalls" de Koenig. C'est un livre semi-canonique donc je suppose que vous avez un point. Cependant, le fait demeure qu'il s'agit d'argot et non d'un terme formel.
Premièrement, Changer ceci en Nous avons donc d'abord ceci: De type Alors ceci: Vous donne: p > Alors ceci: Vous donne: Et ceci : Vous donne: Attribuer cette valeur à un Donc, il affiche 254. Maintenant, dans le deuxième cas, vous commencez par ceci: De ci-dessus, cela assigne ~ 0 appelle comportement non défini car
~ 0
est une valeur entière signée avec tous les bits mis à 1 et vous vous déplacez ensuite à gauche dans le bit de signe. ~ 0u empêche UB mais imprime le même résultat, la question est donc de savoir pourquoi.
unsigned char c = (~0 << 3);
unsigned int
. C'est au moins 16 bits donc la valeur est: 0xfe
0x1ffe
(((~0 << 3) >> 4) << 1)
0x0fff
((~0 << 3) >> 4)
0xfff8
`~0u << 3`
caractère non signé
couper efficacement jusqu'à l'octet de poids faible: 0xffff
0xfff8
à c
qui est tronqué à 0xf8
. Ensuite, >> 4
vous donne 0x0f
et vous donne
0x1e
qui vaut 30.
Le problème est que vous ne pouvez pas raisonner comme vous le faites, car s'il y a un type signé impliqué, il n'y a pas seulement UB, mais aussi un comportement défini par l'implémentation du décalage à droite 0xFF ... FF. Cela peut être un décalage logique ou arithmétique. Mais dans votre exemple, vous forcez le décalage logique en changeant le type en non signé, vous avez donc changé la signification du code.
@Lundin peut-être en général, mais dans ce cas, cela ne change pas ce qui est déplacé dans l'octet d'ordre le plus bas, le résultat est donc le même.
J'ai compilé (avec x86-64 gcc 9.1
) ces deux lignes:
main: push rbp mov rbp, rsp mov BYTE PTR [rbp-1], -2 mov BYTE PTR [rbp-2], -2 mov eax, 0 pop rbp ret
Et j'ai obtenu la sortie d'assembly suivante:
int main() { unsigned char e=(~0 << 1); unsigned char d=(((~0 << 3) >> 4) << 1); }
Comme vous pouvez le voir, les deux lignes sont converties dans la même instruction mov BYTE PTR [rbp-1], -2
. Donc, il semble que le compilateur effectue une optimisation avec votre premier code.
Merci à Thomas Jager pour sa réponse (donnée sur le commentaire de la question)
La solution est simple.
Dans le 1er code, la manipulation des bits est effectuée en prenant l'opérande comme char signé. Pour cette raison, deux nombres binaires complémentaires continuent de changer leur modèle de bits pendant que la manipulation de bits est en cours. Après cela, le nombre de complément à deux résultats est converti en nombre positif avant d'être affecté à la variable non signée c. Le résultat est donc finalement 254.
La question est d'expliquer pourquoi deux sorties de code sont différentes. Nous savons tous que Code 2nd fonctionne bien. Par conséquent, je n'explique que pourquoi le code 1 ne fonctionne pas correctement.
1er code: -
Step 1: ~0 -----> -1 ----(binary form)----> 11111111 with sign bit 1 (means negative) Step 2: (sign bit 1)11111111 << 3 -----shifting to left----> (sign bit 1)11111000 Step 3 ***: (sign bit 1)11111000 >> 4 ----shifing to right-----> (sign bit 1)11111111 *[*** - The left most bits is 1 in Result because of sign extension Sign bit 1 retain its bit to 1 but right shifting the number will append 1 to left most bits without modify sign bit to 0 . Hence all left most bit append to 1 because sign bit 1 is supplying this 1 to left most bits while right shifting ]* Step 4: (sign bit 1)11111111 << 1 ---shifting to left---> (sign bit 1)11111110 Step 5: two complement number (sign bit 1)11111110 converted to positive number by deleting only sign bit to 0. Step 6: Result : (sign bit 0)11111110 ---decimal equivalent---> 254
Le traçage du 1er code est le suivant: - p >
unsigned char c=(((~0 << 3) >> 4) << 1); printf("%d", c);
Je viens d'expliquer sa réponse.
Merci à tous d'avoir fait l'effort de répondre à cette question.
La différence réside dans les types. Voyez ce qui se passe avec
unsigned char c = ((unsigned char) ((unsigned char) (~ 0 << 3) >> 4) << 1);
il existe des conversions arithmétiques par défaut impliquées dans la coercition qui font la différence.
Le calcul du premier code est fait en utilisant le type
int
afin que les bits autres que les 8 bits les moins significatifs soient préservés. Avec le second, les affectations abandonnent toutes sauf les 8 bits les moins significatifs, modifiant ainsi complètement le calcul.@JohnKugelman Beaucoup de matériel d'étude dans la petite FAQ C de SO, y compris Règles de promotion de type implicite , Que sont les opérateurs de décalage de bits (décalage de bits) et comment fonctionnent-ils? et Comportement non défini, non spécifié et défini par l'implémentation .
Bien que dans ce cas, il n'y ait pas beaucoup de conversions implicites sur lesquelles écrire. Il y a une conversion de lvalue en caractère non signé. Sinon, le type du résultat d'une expression de décalage est celui de son opérande gauche (promu).