2017-09-26 4 views
4

j'ai remarqué un comportement étrange avec des crochetsBitshifts annulant au lieu de comportement attendu

opérateurs de bits
#include <stdio.h> 

int  main(void) 
{ 
    unsigned char c; 
    unsigned char d; 

    c = 153; 
    c = (c << 7) >> 7; 
    printf("%d\n", c); 
    d = 153; 
    d = (d << 7); 
    d = (d >> 7); 
    printf("%d\n", d); 
} 

sortie:

153 
1 

Je me attendais c aussi avoir une valeur de 1 ... ce qui se passe sur? Est-ce que ce n'est pas défini?

+2

Jetez un oeil sur le code généré et ce qu'il fait. –

+1

Semblable à [this] (https://stackoverflow.com/questions/33068985/sizeof-an-integer-expression-in-c) question. L'expression est évaluée en tant qu'entier puis stockée en tant que caractère. Versus étant un char à des étapes intermédiaires. – matt

+0

ok qui a du sens, lemmi juste travailler avec typecasting –

Répondre

4

Bit-Shifting un char favorise automatiquement à un int. C'est pourquoi un décalage à gauche de 7 bits ne coupe rien.

Source: section 6.5.7 de the C standard

Les promotions entières sont effectuées sur chacun des opérandes. Le type du résultat est celui de l'opérande gauche promu. Si la valeur de l'opérande de droite est négative ou supérieure ou égale à la largeur de l'opérande gauche promu, le comportement n'est pas défini.

+1

Bonne réponse, et plus rapide que moi, + 1 – gsamaras

+0

ouais merci. J'ai maintenant aussi choisi d'utiliser 'c = (unsigned char) (c << 7) >> 7;' et il me donne la sortie attendue –

2

Il est évalué comme int, puis stocké comme char. Et n'étant pas un char à étape (s) intermédiaire (s). En d'autres termes, lorsque vous décalez un char, il est promu int.

Vérifiez ce que la norme 6.5.7 Bitwise shift operators doit dire:

  • Les promotions entières sont effectuées sur chacun des opérandes.

  • Le type du résultat est celui de l'opérande gauche promu.

  • Si la valeur de l'opérande de droite est négative ou supérieure ou égale à la largeur de l'opérande gauche avancé, le comportement est indéfini.

0

Après ce this comment suggère, nous examinons le code qui est généré sur le x86 plate-forme pour l'expression c = (c << 7) >> 7:

movzbl 31(%esp), %eax ;eax = c 
sall $7, %eax ;left shift 
sarl $7, %eax ;right shift 
movb %al, 31(%esp) ;c = al (one byte) 

Le contenu de c sont chargés dans un 32- registre de bits (eax) et les deux décalages sont effectués sur ce registre. Enfin, l'octet le moins significatif de ce registre (c'est-à-dire al) est affecté à la variable c. En bref, les deux décalages sont évalués comme si les opérandes avaient une largeur de 32 bits.

1

Selon this online c standard draft, opérateurs de décalage au niveau du bit promouvoir les paramètres à un type entier:

6,5.7 Opérateurs de décalage bit à bit

2 Chacun des opérandes doit avoir un type entier.

3 Les promotions entières sont effectuées sur chacun des opérandes.

Alors, quand vous écrivez c = (c << 7) >> 7, la valeur de l'expression c(c << 7) est d'abord converti en une valeur entière, puis déplacé. Par conséquent, aucun des bits se perdent. En les décalant de >> 7, vous obtenez la valeur d'origine. Lorsque vous écrivez d = (d << 7), en revanche, les bits sont perdus une fois que le résultat (intégral) est réaffecté à d, qui est un char non signé et ne peut donc pas contenir les bits "supérieurs" de la valeur intégrale.

1

153 en représentation binaire est 10011001. Les opérandes de l'opérateur de décalage bit à bit doivent avoir le type int, sinon la promotion d'entier aura lieu.

Pour déclaration

c = (c << 7) >> 7; 

c est promu en entier et en supposant 4 octets pour la représentation entière, c sera 00000000 00000000 00000000 10011001 en binaire. Ainsi, l'expression

c = (c << 7) >> 7; // Left and right shift operator will nullify the effect of each other. 

aura pour effet de l'expression

c = c; 

En cas de

d = (d << 7); 
d = (d >> 7); 

Après la première déclaration d aura la valeur (en binaire) 10000000. Après la deuxième déclaration d aura valeur (en binaire) 00000001.