2016-05-04 13 views
-1

Betrachten Sie das folgende Stück Code:Warum gibt 1ul << 64 1 anstelle von 0 zurück?

// Simply loop over until 64 is hit. 
unsigned long x = 0; 
for (int i = 0; i <= 64; i++) { 
    if (i == 64) { 
    x = 1ul << i; 
    printf("x: %d\n", x); 
    } 
} 

Wir wissen, dass unsigned long ist 64 Bit breit und links 1 um 64 Positionen Verschiebung würde 1000 ... 000 (64 Nullen hinter einer) werden, und würde jedoch hat auf 0 abgeschnitten worden ist, gibt den tatsächlichen Ausdruck:

x: 1 

das merkwürdige ist, wenn wir nur

printf("x: %d\n", (1ul << 64)); 

tun würde, es druckt 0

Kann jemand erklären, warum das passiert? Warum erzeugt das Programm im ersten Fall fälschlicherweise 1 statt 0, aber im zweiten Fall ist es richtig?

+0

Warum Schleife 64 mal? Warum nicht einfach "i" gleich 64 setzen? –

+3

Um zu beginnen, verwenden Sie das falsche Format, um einen 'unsigned long'-Wert zu drucken. Sie sollten "% lu" 'verwenden. Siehe z.B. [Diese 'printf'- (und Familien-) Referenz] (http://en.cppreference.com/w/c/io/fprintf). –

+0

@JonnyHenly Wenn ich nur "int i = 64" mache und die Verschiebung und den Druck mache, gibt es 0. Ist das nicht magisch? – OneZero

Antwort

8

Shifting durch die Breite eines Typs oder mehr Ursachen undefined behaviour gemäß §6.5.7/3:

  1. Die ganzzahlige aktionen auf jedem des Operanden durchgeführt werden. Der Typ des Ergebnisses ist der des hochgestuften linken Operanden. Wenn der Wert des der rechte Operand negativ ist oder größer oder gleich der Breite der beworbenen linken Operanden wird das Verhalten undefiniert.

Der Grund dafür ist, dass verschiedene CPUs unterschiedliche Verhalten für übergroße Verschiebungen implementieren und es wäre zu restriktiv gewesen Verhalten für diesen Fall zu definieren - viele Schichten zusätzliche Montage erzeugt müssten. (Obwohl es vielleicht eher implementierungsdefiniert als unbestimmt sein sollte).

Ihre Beobachtung durch den Einsatz von Intel-Familie CPU, die in der Hardware bedeutet „mod 64“ auf der Schaltbreite für einen 64-Bit-Typen erklärt werden kann, so zur Laufzeit 1ul << 64 tat die gleichen wie 1ul << 0 würde; aber im anderen Fall kompilierte der Compiler 1ul << 64 mit arithmetischen Regeln.