-1を使用してすべてのビットをtrueに設定しても安全ですか?
-
03-07-2019 - |
質問
このパターンがCで多く使用されているのを見ました<!> amp; C ++。
unsigned int flags = -1; // all bits are true
これは、これを実現するための優れたポータブルな方法ですか?または、0xffffffff
または~0
を使用していますか?
解決
これは最も簡単なものなので、示したとおりに行うことをお勧めします。 -1
に初期化すると、実際の符号表現とは無関係に常に動作しますが、~
には正しいオペランドタイプが必要になるため、驚くべき動作をすることがあります。その場合のみ、unsigned
タイプの最も高い値を取得できます。
起こりうる驚きの例については、次の例を考えてください:
unsigned long a = ~0u;
すべてのビット1のパターンをa
に保存する必要はありません。ただし、最初にunsigned int
のすべてのビット1でパターンを作成し、次にunsigned long
に割り当てます。 ~0
のビット数が多い場合に起こるのは、それらのすべてが1であるわけではないということです。
そして、これを考えてください。2の補数表現では失敗します:
unsigned int a = ~0; // Should have done ~0u !
その理由は、UINT_MAX
がすべてのビットを反転させる必要があるためです。これを反転すると、2の補数のマシンではULONG_MAX
が得られます(これは必要な値です!)が、別の表現ではflags
が得られません。補数マシンでは、ゼロになります。したがって、1の補数マシンでは、上記は<=>をゼロに初期化します。
理解する必要があるのは、ビットではなく値に関することです。変数は value で初期化されます。初期化子で、初期化に使用される変数のビットを変更すると、それらのビットに従って値が生成されます。 <=>を可能な限り高い値に初期化するために必要な値は、<=>または<=>です。 2番目は<=>のタイプに依存します-<=>には<=>を使用する必要があります。ただし、最初のものはそのタイプに依存しないため、最も高い値を取得するのに適した方法です。
<=>すべてのビットが1であるかどうかについては、 話していません(常にそうであるとは限りません)。また、<=>にすべてのビットが1であるかどうかについては 話していません(もちろんあります)。
しかし、私たちが話しているのは、初期化された<=>変数の結果です。そのため、 <=> のみがすべてのタイプとマシンで機能します。
他のヒント
-
unsigned int flags = -1;
はポータブルです。 -
unsigned int flags = ~0;
は移植性がない 2の補数表現に依存しています。 -
unsigned int flags = 0xffffffff;
は移植性がない 32ビット整数を想定しています。
すべてのビットをC標準で保証された方法で設定する場合は、最初のビットを使用します。
率直に言って、すべてのfffの方が読みやすいと思います。それはアンチパターンであるというコメントについて、すべてのビットが設定/クリアされることを本当に気にするなら、おそらくあなたはおそらくあなたが変数のサイズを気にする状況にあり、ブーストのようなものを必要とするだろうと主張します:: uint16_tなど
上記の問題を回避する方法は、単純に行うことです
unsigned int flags = 0;
flags = ~flags;
ポータブルでポイントまで。
C ++でそもそも、フラグにunsigned intを使用することは良い考えではありません。ビットセットなどはどうですか?
std::numeric_limit<unsigned int>::max()
はunsigned intが32ビット整数であると想定するため、 0xffffffff
の方が適しています。
unsigned int flags = -1; // all bits are true
<!> quot;これは、これを達成するための良い[、]ポータブルな方法ですか?<!> quot;
ポータブル? はい。
いい? 討論可能。このスレッドに示されているすべての混乱から明らかです。仲間のプログラマーが混乱なくコードを理解できるように十分に明確であることは、優れたコードを測定するための寸法の1つです。
また、この方法はコンパイラの警告になりやすいです。コンパイラを損なうことなく警告をなくすには、明示的なキャストが必要です。たとえば、
unsigned int flags = static_cast<unsigned int>(-1);
明示的なキャストでは、ターゲットタイプに注意する必要があります。ターゲットタイプに注意を払っていれば、他のアプローチの落とし穴を自然に回避できます。
ターゲットタイプに注意を払い、暗黙的な変換がないことを確認することをお勧めします。例:
unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);
これらはすべて、同僚のプログラマーにとって正確かつ明白です。
そしてC ++ 11で:auto
を使用して、これらのいずれかをさらに簡単にすることができます:
auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);
単純に正しいことよりも、正しいことは明らかだと思います。
-1を符号なしの型に変換すると、標準により保証され、すべて1になります。 ~0U
のタイプは0
であり、unsigned int
のように明示的に記述しない限り、より大きな符号なしタイプのすべてのビットを埋めることはできないため、~0ULL
の使用は一般に不適切です。健全なシステムでは、~0
は-1
と同一である必要がありますが、標準では1の補数表現と符号/大きさの表現が許可されているため、厳密には移植性はありません。
もちろん、正確に32ビットが必要であることがわかっている場合は0xffffffff
を記述してもかまいませんが、-1には、次のように、型のサイズがわからなくてもどのようなコンテキストでも機能するという利点があります複数のタイプで機能するマクロ、またはタイプのサイズが実装によって異なる場合。タイプがわかっている場合、オールワンを取得する別の安全な方法は、制限マクロUINT_MAX
、ULONG_MAX
、ULLONG_MAX
などです
個人的には常に-1を使用します。それは常に機能し、あなたはそれについて考える必要はありません。
はい。他の回答で述べたように、-1
が最もポータブルです。ただし、セマンティックではなく、コンパイラの警告をトリガーします。
これらの問題を解決するには、次のシンプルなヘルパーを試してください:
static const struct All1s
{
template<typename UnsignedType>
inline operator UnsignedType(void) const
{
return static_cast<UnsignedType>(-1);
}
} ALL_BITS_TRUE;
使用法:
unsigned a = ALL_BITS_TRUE;
uint8_t b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;
インクルードの1つとして#include <limits.h>
がある限り、使用する必要があります
unsigned int flags = UINT_MAX;
長めのビットが必要な場合は、使用できます
unsigned long flags = ULONG_MAX;
これらの値は、符号付き整数の実装方法に関係なく、結果のすべての値ビットが1に設定されることが保証されています。
-1のことはしません。 (少なくとも私には)直感的ではありません。符号なしデータに符号付きデータを割り当てることは、物事の自然な順序に違反しているように見えます。
あなたの状況では、常に0xFFFF
を使用します。 (もちろん、可変サイズに適切な数のFを使用してください。)
[ところで、実際のコードで-1トリックが行われることはめったにありません。]
さらに、変数の個々のビットに本当に関心がある場合は、固定幅のuint8_t
、uint16_t
、uint32_t
タイプの使用を開始することをお勧めします。
IntelのIA-32プロセッサでは、64ビットのレジスタに0xFFFFFFFFを書き込んで、期待どおりの結果を得ることができます。これは、IA32e(IA32の64ビット拡張)が32ビットのイミディエートのみをサポートするためです。 64ビット命令では、32ビット即値は64ビットに符号拡張されます。
次は違法です:
mov rax, 0ffffffffffffffffh
次の例では、RAXに64個の1を入れています。
mov rax, 0ffffffffh
完全を期すために、以下では32個の1をRAX(別名EAX)の下部に置きます。
mov eax, 0ffffffffh
そして実際、64ビット変数に0xffffffffを書き込みたいときにプログラムが失敗し、代わりに0xffffffffffffffffを取得しました。 Cでは、次のようになります。
uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);
結果は次のとおりです。
x is 0xffffffffffffffff
これを0xFFFFFFFFが32ビットと想定しているすべての回答へのコメントとして投稿しようと考えましたが、非常に多くの人がそれに答えたので、別の回答として追加すると思いました。
問題の非常に明確な説明については、litbの回答を参照してください。
私の意見の不一致は、非常に厳密に言えば、どちらの場合でも保証がないということです。すべてのビットが設定されているので、「ビット数の2乗未満」の符号なしの値を表さないアーキテクチャはわかりませんが、実際に規格が述べているのは(3.9.1 / 7 plus注44):
整数型の表現では、純粋な2進記数法を使用して値を定義します。 [注44:] 2進数の0と1を使用する整数の位置表現で、連続するビットで表される値は加算的であり、1で始まり、おそらく2の連続する整数の累乗で乗算されます。最高位。
これにより、ビットのいずれかがすべてである可能性が残ります。
実際:はい
理論的に:いいえ。
-1 = 0xFFFFFFFF(またはプラットフォーム上のintのサイズ)は、2の補数演算でのみ当てはまります。実際には動作しますが、2の補数表現ではなく実際の符号ビットを取得するレガシーマシン(IBMメインフレームなど)があります。提案された〜0ソリューションはどこでも動作するはずです。
0xFFFF
(または0xFFFFFFFF
など)は読みやすいかもしれませんが、コードの移植性を損なう可能性があります。たとえば、特定のビットが設定されているデータ構造内のアイテムの数(呼び出し元によって指定されている正確なビット)をカウントするライブラリルーチンを考えます。このルーチンは、ビットが何を表すかについては完全に不可知かもしれませんが、それでも<!> quot; all bits set <!> quot;が必要です。定数。このような場合、-1は任意のビットサイズで機能するため、16進定数よりもはるかに優れています。
もう1つの可能性は、ビットマスクにtypedef
値が使用される場合、〜(bitMaskType)0を使用することです。ビットマスクが16ビットタイプのみの場合、その式には16ビットのみが設定されます(そうでない場合は 'int'が32ビットであっても)が、16ビットだけで十分なので、問題はありませんただし、タイプキャストで適切なタイプを実際に使用することを提供します。
ちなみに、16進定数が大きすぎてlongvar &= ~[hex_constant]
に収まらないがint
に収まる場合、unsigned int
の形式の式には厄介な落とし穴があります。 longvar &= ~0x4000;
が16ビットの場合、longvar &= ~0x10000
またはlongvar
; longvar &= ~0x8000;
の1ビットをクリアしますが、long
はビット15とそれより上のすべてのビットをクリアします。 <=>に収まる値は、型<=>に補数演算子が適用されますが、結果は<=>に符号拡張され、上位ビットが設定されます。 <=>には大きすぎる値には、補数演算子がタイプ<=>に適用されます。ただし、これらのサイズの間にある値は、補数演算子をタイプ<=>に適用し、その後、符号拡張なしでタイプ<=>に変換されます。
他の人が述べたように、-1はすべてのビットを1に設定して符号なしの型に変換する整数を作成する正しい方法です。ただし、C ++で最も重要なことは正しい型を使用することです。したがって、あなたの問題に対する正しい答え(あなたが尋ねた質問への答えを含む)はこれです:
std::bitset<32> const flags(-1);
これには、必要なビットの正確な量が常に含まれます。他の回答で述べたのと同じ理由で、すべてのビットを1に設定してstd::bitset
を構築します。
-1は常にすべての利用可能なビットが設定されるため、これは確かに安全ですが、私は〜0の方が好きです。 -1はunsigned int
にはあまり意味がありません。 0xFF
...は、型の幅に依存するため、良くありません。
言う:
int x;
memset(&x, 0xFF, sizeof(int));
これにより、常に目的の結果が得られます。
符号なしの型に対してすべてのビットを1に割り当てるという事実を活用することは、与えられた型に対して可能な最大値を取得することと同等です。
質問の範囲をすべての符号なし整数型に拡張します:
-1の割り当ては、あらゆる unsigned 整数型に対して機能します(unsigned int、uint8_t、uint16_tなど)。CとC ++の両方。
代替として、C ++の場合、次のいずれかを実行できます。
-
<limits>
を含めてstd::numeric_limits< your_type >::max()
を使用します
- カスタムテンプレート関数を記述します(これにより、宛先の種類が実際に署名されていない場合などの健全性チェックも可能になります。タイプ)
-1
を割り当てるには常に説明的なコメントが必要になるため、目的はより明確にすることです。
意味をもう少し明確にし、タイプの繰り返しを避ける方法:
const auto flags = static_cast<unsigned int>(-1);
はい、示された表現は非常に正確です
たとえば、ほとんどのマシンでは、整数は2バイト= 16ビットの最大値は2 ^ 16-1 = 65535 2 ^ 16 = 65536です。
0%65536 = 0 -1%65536 = 65535これは1111 ............. 1に対応し、すべてのビットは1に設定されます(残基クラスmod 65536を考慮する場合) したがって、非常に簡単です。
推測
いいえ、この概念を考慮すれば、符号なしintに完全に対応し、実際に動作します
次のプログラムフラグメントを確認するだけ
int main() {
unsigned int a=2;
cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));
unsigned int b=-1;
cout<<"\n"<<b;
getchar();
return 0;
}
b = 4294967295の場合、4バイト整数でwhcihは-1%2 ^ 32です
したがって、符号なし整数に完全に有効です
不一致の場合plzzレポート