2017-01-29 1 views
1

J'ai effectué un petit test sur le décalage de bits en C, et tous les décalages de 0, 8, 16 bits sont corrects et j'ai compris ce qui se passait.bits de décalage à gauche en C

Mais le décalage de 32 bits vers la droite ou la gauche n'est pas clair pour moi, la variable avec laquelle je fais le test est longue de 32 bits.

Puis, j'ai changé les variables de 32 bits qui maintiendraient les résultats de décalage, mais les décalages de gauche à droite de 32 bits sont les mêmes!

Voici mon code:

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <inttypes.h> 

int main() { 
    uint32_t code = 0xCDBAFFEE; 

    uint64_t bit32R = code >> 32; 
    uint16_t bit16R = code >> 16; 
    uint8_t bit8R = code >> 8; 
    uint8_t bit0R = code >> 0; 

    uint64_t bit32L = code << 32; 
    uint16_t bit16L = code << 16; 
    uint8_t bit8L = code << 8; 
    uint8_t bit0L = code << 0; 

    printf("Right shift:\nbit32R %.16x\nbit16R %x\nbit8R %x\nbit0R %x\n\n", 
      bit32R, bit16R, bit8R, bit0R); 
    printf("Left shift:\nbit32L %.16x\nbit16L %x\nbit8L %x\nbit0L %x\n\n", 
      bit32L, bit16L, bit8L, bit0L); 
} 

Voici le résultat que je reçois:

Right shift: 
bit32R 00000000cdbaffee 
bit16R 0 
bit8R cdba 
bit0R ff 

Left shift: 
bit32L 00000000cdbaffee 
bit16L 0 
bit8L 0 
bit0L 0 

Process returned 61 (0x3D) execution time : 0.041 s 
Press any key to continue. 
+2

Votre code appelle un comportement indéfini. Une valeur de décalage de '32' est trop grande pour un' int' de 32 bits (même chose pour 16 bits si votre plate-forme a 16 bits 'int'). Et utilisez les macros 'PRInX' de' inttypes.h' pour imprimer des types à largeur fixe. Vous avez une mauvaise idée du fonctionnement des opérations entières. – Olaf

+0

J'ai changé le fichier include en stdint! Ce qui n'a rien changé, mais j'ai oublié stdint, et inclus inttypes. Mais je pense qu'ils sont les mêmes, au moins pour mon code. –

+0

Peut-être avant de faire des suppositions, c'est une bonne idée de lire ce que les en-têtes fournissent, comment ils sont liés et ce que signifie PRInX? – Olaf

Répondre

6

Le décalage à droite d'un nombre de bits égal ou supérieur à sa taille est un comportement indéfini.

C11 6.5.7 opérateurs de décalage au niveau du bit

Syntaxe

shift-expression: additive-expression 
    shift-expression << additive-expression 
    shift-expression >> additive-expression 

Contraintes

Chacun des opérandes ont type entier.

Sémantique

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.

Le résultat de E1 << E2 est E1 décalé vers la gauche E2 positions de bit; les bits vides sont remplis de zéros. Si E1 a un type non signé, la valeur du résultat est E1 × 2 E2, modulo réduit d'un de plus que la valeur maximale représentable dans le type de résultat.Si E1 a un type signé et une valeur non négative, et E1 × 2 E2 est représentable dans le type de résultat, alors c'est la valeur résultante; sinon, le comportement est indéfini.

Le résultat de E1 >> E2 est E1 décalé à droite E2 positions de bit. Si E1 a un type non signé ou si E1 a un type signé et une valeur non négative, la valeur du résultat est la partie intégrale du quotient de E1/2 E2. Si E1 a un type signé et une valeur négative, la valeur résultante est définie par l'implémentation.

La taille de int sur votre plate-forme semble être à la plupart des 32 bits, de sorte que les initialisations pour bit32R et bit32L ont un comportement non défini.

Les expressions 64 bits doivent être écrits:

uint64_t bit32R = (uint64_t)code >> 32; 

et

uint64_t bit32L = (uint64_t)code << 32; 

De plus, les formats utilisés dans printf ne sont pas correctes pour les arguments transmis (à moins int a 64 bits, ce qui produirait une sortie différente).

Votre compilateur ne semble pas entièrement compatible C99, vous devez ajouter une instruction finale return 0; à la fin du corps de la fonction main().

Voici une version corrigée:

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 
#include <inttypes.h> 

int main(void) { 
    uint32_t code = 0xCDBAFFEE; 

    uint64_t bit32R = (uint64_t)code >> 32; 
    uint16_t bit16R = code >> 16; 
    uint8_t bit8R = code >> 8; 
    uint8_t bit0R = code >> 0; 

    uint64_t bit32L = (uint64_t)code << 32; 
    uint16_t bit16L = code << 16; 
    uint8_t bit8L = code << 8; 
    uint8_t bit0L = code << 0; 

    printf("Right shift:\n" 
      "bit32R %.16"PRIx64"\n" 
      "bit16R %"PRIx16"\n" 
      "bit8R %"PRIx8"\n" 
      "bit0R %"PRIx8"\n\n", 
      bit32R, bit16R, bit8R, bit0R); 

    printf("Left shift:\n" 
      "bit32L %.16"PRIx64"\n" 
      "bit16L %"PRIx16"\n" 
      "bit8L %"PRIx8"\n" 
      "bit0L %"PRIx8"\n\n", 
      bit32L, bit16L, bit8L, bit0L); 

    return 0; 
} 

La sortie est:

Right shift: 
bit32R 0000000000000000 
bit16R cdba 
bit8R ff 
bit0R ee 

Left shift: 
bit32L cdbaffee00000000 
bit16L 0 
bit8L 0 
bit0L ee 

cela pourrait ne pas être ce que vous attendez, parce que les types des variables sont quelque peu contradictoires.

+0

retourner 0 pour main() dans C99 n'a jamais été obligatoire, il est dit que si omis la valeur de retour de 0 serait prévu fourni par le compilateur (prototype de main() fuction dépend aussi de la plate-forme, void principal (vide) également acceptable). Il y a très peu de compilateurs entièrement compatibles C99 et encore moins de C++ 11 en ce moment, en particulier sur les plates-formes embarquées. Je n'étais même pas sûr qu'il avait accès aux macros PRIx, le compilateur que j'avais à portée de main ne les avait pas en en-tête – Swift

+0

@Swift: le statut imprimé par l'environnement en cours d'exécution 'Processed return 61 (0x3D)' indique que le compilateur * not ** ajoute le 'return 0; 'implicite mandaté par C99, d'où ma déclaration sur le compilateur non-conforme. Je ne serais pas surpris si cet environnement est basé sur MSVC. Dans tous les cas, il est bon de toujours retourner 0 (ou un statut de sortie significatif) à la fin de 'main()'. La sémantique de retour implicite C99 est un correctif boiteux pour le code bâclé IMHO. – chqrlie

+0

Absolument! Cela a fonctionné parfaitement. Mais pourquoi le (% .16x) ne fonctionnait pas pour la taille variable 64? Il montre la taille hexadécimale de 64 bits mais le décalage n'est pas correct. Et avec PRIx64, fonctionne bien? –

1

Un problème est que vous utilisez %x pour imprimer un entier de 64 bits. Vous devez utiliser le spécificateur de format correct pour chaque variable. Il y a des macros pour ce disponibles:

#define __STDC_FORMAT_MACROS 
#include <inttypes.h> 

// ... 

printf("64 bit result: %" PRIx64 "\n", bit32R); 
printf("16 bit result: %" PRIx16 "\n", bit16R); 
printf("8 bit result: %" PRIx8 "\n", bit8R); 

Plus d'informations peuvent être trouvées here.

+0

Bien que '% x' soit incorrect, l'utilisation de PRIx64 ne modifie pas le résultat du décalage. –

+0

Si PRIx64 ne change pas le résultat, alors pourquoi% x est-il faux? Je reçois les mêmes résultats avec votre modification. Je peux même obtenir le format hexadécimal complet de 64 bits avec le modificateur% .16x. –

+0

@Perch ce sont des choses spécifiques à la plate-forme. En général, il semble fonctionner, mais échoue sur d'autres plates-formes ou échoue lorsque vous écrivez du code plus avancé. J'avoue que je ne me soucie pas d'utiliser toutes ces macros quand j'écris du code, mais vous ne devriez les ignorer que si vous savez toujours sur quelle plateforme vous allez tourner. Sinon, un désastre se produira. –

0

Vous ne faites pas de 64 bits à gauche, car le code est uint32_t, donc le compilateur utilise la version 32bit de l'opérateur. En outre, vous devriez dire imprimer utiliser long long (comme uint64_t)

#include <cstdint> 
#include <stdio.h> 
int main() 
{ 
uint32_t code = 0xCDBAFFEE; 


uint64_t bit32R=((uint64_t)code)>>32; 
uint16_t bit16R=code>>16; 
uint8_t bit8R=code>>8; 
uint8_t bit0R=code>>0; 

uint64_t bit32L=((uint64_t)code)<<32; 
uint16_t bit16L=code<<16; 
uint8_t bit8L=code<<8; 
uint8_t bit0L=code<<0; 


printf("Right shift:\nbit32R %llx\nbit16R %x\nbit8R %x\nbit0R %x\n\n", bit32R,bit16R,bit8R,bit0R); 
printf("Leftt shift:\nbit32L %llx\nbit16L %x\nbit8L %x\nbit0L %x", bit32L,bit16L,bit8L,bit0L); 
} 

Le résultat est:

Right shift: 
bit32R 0 
bit16R cdba 
bit8R ff 
bit0R ee 

Leftt shift: 
bit32L cdbaffee00000000 
bit16L 0 
bit8L 0 
bit0L ee 

Vous devez utiliser les macros définies dans inttypes.h si vous avez C99 compilateur compatible, malheureusement certaines plates-formes n'ont pas ces définitions. Les descripteurs de format pour printf dépendent de la plate-forme.

+1

OP ne prétend pas faire 64 bits. De plus, 64 bits ne sont pas longs sur toutes les plateformes. –

+0

@Olaf Dietsche: c'était à peu près la réclamation. uint64_t bit32L = code << 32; est identique à uint32_t bit32L = code << 32. Mais je suis d'accord, bien que basé sur la réaction de sa sortie printf, _his_ plate-forme a 64 bits aussi longtemps. – Swift

+0

Mais il y a uint64_t qui vous donne une variable 64 bits. –