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でテスト済みです。
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でテスト済みです。
6.5.7ビット単位のシフト演算子:
右オペランドの値が負であるか、より大きいかまたは促進左オペランドの幅に等しい場合、動作は未定義です。
コンパイラは、明らかに何かを行うライセンスがありますが、最も一般的な動作は、式(とそれに依存するもの)を完全に排除することです。または、範囲のシフト。多くのハードウェアプラットフォーム(x86およびARMを含む)は、シフト量として使用するためにいくつかの下位ビットをマスクします。実際のハードウェア命令は、シフト量がゼロにマスクされているため、これらのプラットフォームのいずれかで観測している結果を返します。したがって、あなたの場合、コンパイラはシフトを最適化しているかもしれませんし、単純にハードウェアに何かをさせるだけかもしれません。いずれかを知りたい場合は、アセンブリを検査してください。
@MooingDuck:あなたは観察された振る舞いからそれを推論することはできません。他にもいくつかの説明があります。 –
私は、OPの動作を説明するRHSの右端の5ビットを使用するだけで、この動作を実装するコンパイラがあると聞いています。 –
@StevemCanon:そうです。それは純粋な推測です。 –
標準に従って、実際に存在するビット以上にシフトすると、未定義の動作が発生する可能性があります。だから私たちはそのためにコンパイラを責めることはできません。
モチベーションは、おそらく0x80000000の "境界の意味"にあります。これは、最大正と負の境界にあります(そして、最も高いビットが設定された "負"です)。コンパイルされたプログラムは、「不可能な」事柄を検証する時間を無駄にしないようにしなければならない(実際にプロセッサに30億回のビットシフトをさせたいのですか?)。
"結果できません"、 "結果がありません"。 –
@R ..:あなたの側に依存しています:コンパイラのスタンドポイントで "結果"を意味する(言語が何も言わないという意味で)コンパイラ設計者がそれを "定義"できるという感覚) –
ではなく、は多少のビットシフトを試みています。お使いのシステム上の
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
タイプを持っていますが、しない限り、実世界のコンパイラはそれをしません。)
未定義の動作を避ける唯一の方法は、あなたが何をしようとして行うことではありません。
でも可能ですが、恐らくほとんどありませんが、
オリジナルのコードの動作は未定義ではありません。 int
へunsigned int
から0x80000000
の実装定義の変換は範囲0の値をもたらす場合にのみint
が32ビットよりも小さい場合、変換がうまくそれを読み取る0
最後の段落はそれほど恐ろしいことではありません.16ビットのint型のシステムでは、まったくそうではないでしょう。 – caf
@caf:確かに、私はそれを考えていたはずです。 –
をもたらす可能性がある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" であること。その空白は元のチェーンビットの最後のビットを削除します
技術的には、(gccが警告で報告する)タイプの幅を超えてシフトしているので、未定義の動作です。 –
キャストしない限り、それは-1だけシフトするように要求していないでしょうか? – Marvo
また、gccはUBの精神を完全に尊重しているようですが、条件によって違う「間違った」結果を出すようになっています(http://gcc.gnu.org/ml/gcc/2004-11/msg01131.html) –