2011-09-29 13 views
14

0より大きい値で右にシフトされた番号1は、0にする必要があります。しかし、私はこの非常に単純なプログラムを入力することで、1を印刷することができます。なぜ(1 >> 0x80000000)== 1ですか?

#include <stdio.h> 

int main() 
{ 
     int b = 0x80000000; 
     int a = 1 >> b; 
     printf("%d\n", a); 
} 

linuxでgccでテスト済みです。

+27

技術的には、(gccが警告で報告する)タイプの幅を超えてシフトしているので、未定義の動作です。 –

+2

キャストしない限り、それは-1だけシフトするように要求していないでしょうか? – Marvo

+3

また、gccはUBの精神を完全に尊重しているようですが、条件によって違う「間違った」結果を出すようになっています(http://gcc.gnu.org/ml/gcc/2004-11/msg01131.html) –

答えて

30

6.5.7ビット単位のシフト演算子:

右オペランドの値が負であるか、より大きいかまたは促進左オペランドの幅に等しい場合、動作は未定義です。

コンパイラは、明らかに何かを行うライセンスがありますが、最も一般的な動作は、式(とそれに依存するもの)を完全に排除することです。または、範囲のシフト。多くのハードウェアプラットフォーム(x86およびARMを含む)は、シフト量として使用するためにいくつかの下位ビットをマスクします。実際のハードウェア命令は、シフト量がゼロにマスクされているため、これらのプラットフォームのいずれかで観測している結果を返します。したがって、あなたの場合、コンパイラはシフトを最適化しているかもしれませんし、単純にハードウェアに何かをさせるだけかもしれません。いずれかを知りたい場合は、アセンブリを検査してください。

+0

@MooingDuck:あなたは観察された振る舞いからそれを推論することはできません。他にもいくつかの説明があります。 –

+0

私は、OPの動作を説明するRHSの右端の5ビットを使用するだけで、この動作を実装するコンパイラがあると聞いています。 –

+0

@StevemCanon:そうです。それは純粋な推測です。 –

2

標準に従って、実際に存在するビット以上にシフトすると、未定義の動作が発生する可能性があります。だから私たちはそのためにコンパイラを責めることはできません。

モチベーションは、おそらく0x80000000の "境界の意味"にあります。これは、最大正と負の境界にあります(そして、最も高いビットが設定された "負"です)。コンパイルされたプログラムは、「不可能な」事柄を検証する時間を無駄にしないようにしなければならない(実際にプロセッサに30億回のビットシフトをさせたいのですか?)。

+2

"結果できません"、 "結果がありません"。 –

+0

@R ..:あなたの側に依存しています:コンパイラのスタンドポイントで "結果"を意味する(言語が何も言わないという意味で)コンパイラ設計者がそれを "定義"できるという感覚) –

1

ではなく、は多少のビットシフトを試みています。お使いのシステム上の

INT_MAXは(私は累乗を表すために**を使用しています)おそらく2**31-1、または0x7fffffffです。それは、その後の宣言では、ケースの場合:

int b = 0x80000000; 

;定数0x80000000はタイプunsigned int、ないintである(問題のセミコロンが欠落していたあなたの正確なコードをコピーアンドペーストしてください)。値は暗黙的にintに変換されます。結果はintの範囲外であるため、結果は実装定義です(または、C99では実装定義のシグナルが発生する可能性がありますが、それを行う実装についてはわかりません)。

これが行われる最も一般的な方法は、符号なしの値のビットを2の補数の符号付き値として再解釈することです。この場合の結果は-2**31または-2147483648です。

だから、行動は、あなたがタイプintの幅と等しいか、超えた値でシフトしているので、あなたは(非常に大きい)値によりシフトしているので、それは未定義だ未定義ではありません。

もちろん、それは重要ではありません。 undefinedは未定義です。

注:上記は、intがシステム上で32ビットであることを前提としています。 intが32ビットより広い場合、そのほとんどは適用されません(ただし、動作は未定義です)。

あなたが本当に0x80000000ビットだけシフトしようとしたい場合、あなたはこのようにそれを行うことができます:

unsigned long b = 0x80000000; 
unsigned long a = 1 >> b; // *still* undefined 

unsigned longは、値0x80000000を保持するのに十分な大きさが保証されている問題の一部を避けるように、 。

もちろん、0x80000000はunsigned longの幅以上であるため、シフトの動作は元のコードと同じように定義されていません。 (あなたのコンパイラが本当に大きなunsigned longタイプを持っていますが、しない限り、実世界のコンパイラはそれをしません。)

未定義の動作を避ける唯一の方法は、あなたが何をしようとして行うことではありません。

でも可能ですが、恐らくほとんどありませんが、 オリジナルのコードの動作は未定義ではありません。 intunsigned intから0x80000000の実装定義の変換は範囲0の値をもたらす場合にのみintが32ビットよりも小さい場合、変換がうまくそれを読み取る0

+0

最後の段落はそれほど恐ろしいことではありません.16ビットのint型のシステムでは、まったくそうではないでしょう。 – caf

+0

@caf:確かに、私はそれを考えていたはずです。 –

0

をもたらす可能性がある31 ..発生する可能性が多分すぎで式1をシフト避けるために、式2あなた

式1 >> expression2の

>>オペレータマスクを助けることができます。

シフト量がexpression1のデータ型のビット数を超えた場合、すべての元のビットがシフトされて些細な結果になるためです。一つ少なく有する(ビット単位のAND演算子を使用して)

マスク式2: シフト演算子が実際のシフト量を算出するために、次の式を使用し、各シフトは、元のビットの少なくとも一つを残すことを保証するための今

式1のビット数よりも大きくなります。

var x : byte = 15; 
// A byte stores 8 bits. 
// The bits stored in x are 00001111 
var y : byte = x >> 10; 
// Actual shift is 10 & (8-1) = 2 
// The bits stored in y are 00000011 
// The value of y is 3 
print(y); // Prints 3 

operacionは7ビットであろうように、xが8バイトであるので、 "8-1" であること。その空白は元のチェーンビットの最後のビットを削除します

関連する問題