77 votes

Pourquoi ne pas laissé de décalage de bit, "<<"pour les nombres entiers de 32 bits fonctionne comme prévu lorsque utilisé plus de 32 fois?

Quand j'écris le programme suivant et utiliser le compilateur C++ de GNU, la sortie est 1 , ce qui je pense est dû à la rotation de l'opération effectuée par le compilateur.

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    int a = 1;
    cout << (a << 32);

    return 0;
}

Mais logiquement, comme il est dit que les bits sont perdues si elles débordent le peu de largeur, la sortie doit être de 0. Ce qui se passe?

Le code est sur ideone, http://ideone.com/VPTwj.

54voto

Ashwin Points 17537

Cela est dû à une combinaison d'un comportement indéfini dans C et la vague du comportement de certains architecture IA-32 opérations. Pour plus de détails, voir mon ancien blog ici.

48voto

Lindydancer Points 13353

En C++, maj n'est bien défini si vous passez une valeur de moins d'étapes que la taille du type. Si int est de 32 bits, puis seulement 0 et y compris le 31 étapes bien définies.

Alors, pourquoi est-ce?

Si vous prenez un coup d'oeil au matériel sous-jacent qui effectue la maj, si elle n'a qu'à regarder le bas de cinq bits de valeur (dans les 32 bits de cas), il peut être mis en œuvre en utilisant moins de portes logiques que si elle a pour inspecter tous les bits de la valeur.

La réponse à la question en commentaire

Le C et le C++ sont conçus pour courir aussi vite que possible, sur tout le matériel disponible. Aujourd'hui, le code généré est tout simplement un "shift" de l'enseignement, quelle que soit la façon dont le matériel sous-jacent gère les valeurs hors de la plage spécifiée. Si les langues ont précisé comment maj devrait se comporter, le produit pourrait faudrait vérifier que le décalage est dans la gamme avant d'effectuer le changement. Généralement, cela donnerait trois instructions (comparer, de direction, de travail). (Il est vrai que, dans ce cas, il ne serait pas nécessaire que le décalage est connu).

24voto

David Heffernan Points 292687

C'est un comportement indéterminé selon la norme C++:

La valeur de E1 << E2 E1 gauche décalée E2 positions de bits; vacant les bits sont à zéro rempli. Si E1 a un type non signé, la valeur du résultat est E1 × 2E^2, la réduction modulo un plus que la valeur maximale représentable dans le type de résultat. Sinon, si E1 a un type signé et non négatif valeur, et E1×2E^2 est représentable dans le type de résultat, alors que c'est le valeur qui en résulte; sinon, l' le comportement est indéfini.

13voto

antonakos Points 4921

Les réponses de Lindydancer et 6502 d'expliquer pourquoi, sur certaines machines), il arrive à être un 1 qui est en cours d'impression (bien que le comportement de l'opération n'est pas défini). Je suis ajoutant des détails dans le cas où ils ne sont pas évidents.

Je suis en supposant que (comme moi), vous exécutez le programme sur un processeur Intel. GCC génère ces instructions de montage pour l'opération de décalage:

movl $32, %ecx
sall %cl, %eax

Sur le thème de l' sall et d'autres opérations de décalage, page 624 dans le Jeu d'Instructions du Manuel de Référence dit:

Le 8086 ne masque pas le décalage. Cependant, tous les autres de l'Architecture Intel processeurs (en commençant avec l'Intel 286) ne masque le décalage de cinq bits, résultant en une nombre maximal de 31. Ce masque est fait dans tous les modes de fonctionnement (y compris le virtuel 8086 mode) afin de réduire au maximum les temps d'exécution des instructions.

Depuis le bas du 5 de 32 bits sont égaux à zéro, 1 << 32 est équivalent à 1 << 0, ce qui est 1.

Expérimenter avec de plus grands nombres, nous prédire que

cout << (a << 32) << " " << (a << 33) << " " << (a << 34) << "\n";

imprimez 1 2 4, et en effet, c'est ce qui se passe sur ma machine.

11voto

6502 Points 42700

Il ne fonctionne pas comme prévu parce que vous êtes en attendre trop.

Dans le cas de x86 le matériel ne se soucient pas des opérations de déplacement où le compteur est plus grand que la taille du registre (voir, par exemple, SHL instruction description sur x86 documentation de référence pour une explication).

La norme C++ ne voulais pas imposer un coût supplémentaire en disant que faire dans ces cas parce que le code généré aurait été obligé d'ajouter des contrôles supplémentaires et de la logique pour chaque paramétrique maj.

Avec cette liberté de mise en œuvre de compilateurs peuvent générer seulement une seule instruction d'assemblage sans aucun test ou de la branche.

Un de plus "utile" et "logique" aurait été par exemple de disposer (x << y) équivalent à (x >> -y) et aussi la manipulation de la haute compteurs avec une logique et la cohérence du comportement.

Cependant, cela aurait exigé beaucoup plus lente de manutention pour le décalage de bits, donc le choix a été de faire ce que fait le matériel, quitte à ce que les programmeurs ont besoin d'écrire leur propres fonctions pour les valises latérales.

Étant donné que différents matériels n'choses différentes dans ces cas, ce que dit la norme est essentiellement "ce qui se passe quand vous faites des choses étranges juste ne blâme pas le C++, c'est de ta faute", traduit en jargon juridique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X