2016-06-21 17 views
5

Ich habe einen dummen Test über Bit-Manipulation gemacht, und ich habe dieses Problem gefunden. Ich führe diesen Code aus:Linke Shift-Bits in c

Natürlich weiß ich, dass wenn i> 31, ein Überlauf erzeugt wird. Ich denke, dass beide Teile des Codes (way1 und way2) das gleiche Ergebnis ausgeben sollten. Aber ich habe diese (am Ende):

/* ... correct results from i=1 to i=31 ... */ 
1<<30 
mul: 0x40000000 , val: 0x40000000 

1<<31 
mul: 0x80000000 , val: 0x80000000 

1<<32 
mul: **0x0** , val: **0x1** 

1<<33 
mul: **0x0** , val: **0x2** 

Warum, wenn beiden Befehle Verschiebungen bleiben, das Programm verschiedene Ausgaben erzeugt? Es scheint, dass der Part way2 eine runde Verschiebung erzeugt, aber ich weiß nicht warum, ich glaube wirklich, dass "mul" immer den richtigen Wert hat.

ich unter einem Intel 32-Bit-Maschine, gcc Version 4.4.7

+5

Das Ergebnis einer Schiebeoperation mit einem Verschiebungsbetrag größer als die Operandenbreite ist nach C-Standard nicht definiert. –

+0

Nur eine Meinung, aber ich würde sagen, dass in diesen Fällen: 'mul << one' und' one << i, es ist eigentlich besser, einfach '1' zu verwenden, als ihm den Namen' one' zu ​​geben. –

+0

Das ist das Problem, ja. In diesem Fall verknüpft es wahrscheinlich den rechten Operanden mit 0x3F, bevor das Shft ausgeführt wird. –

Antwort

2

Bei

val = (one<<i); 

wenn i zu 32 größer oder gleich kommt, ist das Verhalten undefiniert.

 

Jedoch im Fall von

while (temp--) 
    mul = mul << one; 

für Verschiebungen von mehr als 32, wird es Null verschieben und das Ergebnis definiert ist (Null).

+0

Ja. Ich glaube, Du hast recht. Mit der Antwort von Aif denke ich es ist klar. Danke vielmals! – Alexi

9

Wahrscheinlich, weil das ist nicht definiertes Verhalten kompilieren? Nach §6.5.7:

Wenn der Wert des rechten Operanden negativ ist oder größer oder gleich die Breite des linken Operanden gefördert wird, ist das Verhalten undefiniert.

+0

Nun, ich könnte etwas von beiden Verhalten erwarten, aber nur eines von beiden in beiden Fällen. Denkst du nicht, dass beide Operationen gleich sind? EDIT: Ich sehe die Deepmax Antwort und ich verstehe dich jetzt. Vielen Dank! – Alexi

+0

@Alexi Verstehst du die Bedeutung von etwas undefiniert zu sein? –

+0

@EugeneSh. Ja, mein Problem war, dass ich die Bedeutung "undefiniertes Verhalten" verstand, aber ich wusste nicht, warum es zwei verschiedene "undefinierte Verhaltensweisen" mit der (scheinbar) gleichen Operation gab. – Alexi

-3

Wenn ich Ihr Code mit -Wall kompilieren Ich habe Beschwerden:

BASH> gcc -Wall left-shift.c 
left-shift.c: In function ‘main’: 
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 3 has type ‘long unsigned int’ [-Wformat=] 
    printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
      ^
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Wformat=] 

Also änderte ich printf

printf(" \n 1<<%i \n mul: 0x%lX , val: 0x%lX\n",i, mul, val); 

Mit dieser Änderung "mul" und "val" zeigt die gleichen Ergebnisse:

1<<30 
mul: 0x40000000 , val: 0x40000000 

1<<31 
mul: 0x80000000 , val: 0x80000000 

1<<32 
mul: 0x100000000 , val: 0x100000000 

1<<33 
mul: 0x200000000 , val: 0x200000000 

Systeminformationen:

BASH> gcc --version 
gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 
Copyright (C) 2015 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
BASH> uname -a 
Linux bm-pc-ubuntu 4.4.0-24-generiC#43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 
BASH> lsb_release --all 
No LSB modules are available. 
Distributor ID: Ubuntu 
Description: Ubuntu 16.04 LTS 
Release: 16.04 
Codename: xenial 
+0

Dies liegt daran, dass Ihr Computer 64-Bit und sein 32-Bit ist. – s7amuser

+0

diese Warnung ignorieren, gibt mir sogar strnager Ausgang als die ursprünglichen: 1 << 30 mul: 0x40000000, val: 0x40000000 1 << 31 mul: 0x80000000, val: 0x80000000 1 << 32 mul : 0x0, val: 0x0 1 << 33 mul: 0x0, val: 0x0 –

+0

Sie sollten die Warnung nicht ignorieren. Wenn Sie Ergebnisse sehen wollen, die dem OP ähnlich sind, versuchen Sie den Schleifenzähler auf 66 zu erhöhen. – s7amuser

0

Wenn Sie dies tun:

val = (one<<i); 

Sie von i eine einzige Linksverschiebung zu tun. Wenn i größer als 31 ist, ergibt dies undefined behavior, was bedeutet, dass das Ergebnis möglicherweise das ist, was Sie erwarten.

Aus dem Bereich 6.5.7.3 der C standard:

Die ganze Zahl Aktionen sind auf jedem des Operanden durchgeführt. Der Typ des Ergebnisses ist der des hochgestuften linken Operanden.Wenn der Wert des rechten Operanden negativ ist oder größer als oder gleich der Breite des heraufgestuften linken Operanden ist, ist das Verhalten undefined.

jedoch, wenn Sie dies tun:

while (temp--) 
    mul = mul << one; 

Sie sind von 1 i mal eine Linksverschiebung zu tun. Dies ist gut definiert und gibt Ihnen den Wert, den Sie erwarten.

Sie verwenden auch %X, um eine long zu drucken, wenn Sie %lX verwenden sollten. Dies verursacht auch undefiniertes Verhalten.