私はこのパターンがC&C++で多く使用されるのを見てきました。
unsigned int flags = -1; // all bits are true
これは、これを実現するための優れたポータブルな方法ですか?または0xffffffff
または~0
を使用していますか?
unsigned int flags = -1;
はポータブルです。unsigned int flags = ~0;
は2の補数表現に依存しているため、移植性がありません。unsigned int flags = 0xffffffff;
は、32ビット整数を想定しているため、移植性がありません。C標準で保証されている方法ですべてのビットを設定する場合は、最初のビットを使用します。
率直に言って、すべてのfffの方が読みやすいと思います。それはアンチパターンであるというコメントについて、すべてのビットが設定/クリアされることを本当に気にするなら、おそらくあなたはおそらくあなたが変数のサイズを気にする状況にあると主張するでしょう、それはブーストのような何かを必要とするでしょう:: uint16_tなど.
言及された問題を回避する方法は、単純に行うことです:
unsigned int flags = 0;
flags = ~flags;
ポータブルでポイントまで。
フラグにunsigned intを使用することは、C++のそもそもの良い考えだとは思いません。ビットセットなどはどうですか?
std::numeric_limit<unsigned int>::max()
は、0xffffffff
は、unsigned intが32ビット整数であると想定しています。
unsigned int flags = -1; // all bits are true
「これは、これを達成するための優れた[、]ポータブルな方法ですか?」
ポータブル? はい。
良い? 討論可能、このスレッドで示されているすべての混乱から明らかなように。あなたの仲間のプログラマが混乱なくコードを理解できるように十分に明確であることは、私たちが良いコードのために測定する次元の1つであるべきです。
また、このメソッドはcompiler warningsになりやすいです。コンパイラを損なうことなく警告をなくすには、明示的なキャストが必要です。例えば、
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
{
static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
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ビットの即値はsign-extendedから64ビットになります。
以下は違法です:
mov rax, 0ffffffffffffffffh
以下は、64個の1をRAXに入れます。
mov rax, 0ffffffffh
完全を期すために、以下ではRAX(別名EAX)の下部に32個の1を入れます。
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ビットを想定しているというすべての回答へのコメントとしてこれを投稿しようと考えましたが、非常に多くの人々がそれに答えたので、別の回答として追加すると思いました。
0xFFFF
(または0xFFFFFFFF
など)は読みやすいかもしれませんが、そうでなければ移植性のあるコードの移植性を損なう可能性があります。たとえば、特定のビットが設定されているデータ構造内のアイテムの数をカウントするライブラリルーチンを考えてみましょう(呼び出し元によって指定された正確なビット)。ルーチンは、ビットが何を表すかについて完全に不可知かもしれませんが、それでも「すべてのビットを設定する」定数を持つ必要があります。このような場合、-1は16進定数よりもはるかに優れています。これは、ビットサイズに関係なく機能するためです。
もう1つの可能性は、ビットマスクにtypedef
値が使用される場合、〜(bitMaskType)0;を使用することです。ビットマスクが16ビットタイプのみである場合、その式には16ビットのみが設定されます(そうでない場合は 'int'が32ビットであっても)が、16ビットがすべて必要なため、問題はありません提供タイプキャストで適切なタイプを実際に使用すること。
ちなみに、16進定数が大きすぎてint
に収まらないがlongvar &= ~[hex_constant]
に収まる場合、unsigned int
という形式の式には厄介な問題があります。 int
が16ビットの場合、longvar &= ~0x4000;
またはlongvar &= ~0x10000
; longvar
の1ビットをクリアしますが、longvar &= ~0x8000;
はビット15とそれより上のすべてのビットをクリアします。 int
に収まる値の補数演算子はint
型に適用されますが、結果は上位ビットを設定してlong
に符号拡張されます。 unsigned int
には大きすぎる値には、long
型に適用される補数演算子があります。ただし、これらのサイズの間にある値は、unsigned int
型に補数演算子を適用し、符号拡張なしでlong
型に変換されます。
問題の非常に明確な説明については、litbの回答を参照してください。
私の意見の不一致は、非常に厳密に言えば、どちらの場合にも保証がないということです。すべてのビットが設定されているので、「ビット数の2乗未満」の符号なしの値を表さないアーキテクチャは知りませんが、ここに標準が実際に言っているものがあります(3.9.1/7 plus注44):
整数型の表現では、純粋な2進記数法を使用して値を定義する必要があります。 [注44:] 2進数の0と1を使用する整数の位置表現。連続するビットで表される値は加算的で、1で始まり、おそらく2の連続する整数の累乗で乗算されます。最高位。
そのため、ビットの1つがまったく何でもない可能性があります。
実際:はい
理論的に:いいえ。
-1 = 0xFFFFFFFF(またはプラットフォームのintのサイズ)は、2の補数演算でのみ当てはまります。実際には動作しますが、2の補数表現ではなく実際の符号ビットを取得するレガシーマシン(IBMメインフレームなど)があります。提案された〜0ソリューションはどこでも機能するはずです。
他の人が述べたように、-1はすべてのビットを1に設定して符号なしの型に変換する整数を作成する正しい方法です。ただし、C++で最も重要なことは正しい型を使用することです。したがって、あなたの問題に対する正しい答え(あなたが尋ねた質問への答えを含む)はこれです:
std::bitset<32> const flags(-1);
これには、必要なビットの正確な量が常に含まれます。他の回答で述べたのと同じ理由で、すべてのビットを1に設定してstd::bitset
を構築します。
意味をもう少し明確にする方法と、タイプの繰り返しを避ける方法:
const auto flags = static_cast<unsigned int>(-1);
-1は常にすべての使用可能なビットが設定されるため、これは確かに安全ですが、私は〜0が好きです。 -1はunsigned int
に対してあまり意味がありません。 0xFF
...は、型の幅に依存するため、良くありません。
符号なしの型に対してすべてのビットを1に割り当てるという事実を活用することは、与えられた型に対して可能な最大値を取ることと同等です。
そして質問の範囲をすべてのunsigned整数型に拡張する:
-1の割り当ては、CとC++の両方の任意のunsigned整数型 (unsigned int、uint8_t、uint16_tなど) 。
別の方法として、C++の場合、次のいずれかを実行できます。
<limits>
_を含め、std::numeric_limits< your_type >::max()
を使用します_-1
_を割り当てるには常に説明的なコメントが必要になるため、目的はより明確にすることです。
私は言う:
int x;
memset(&x, 0xFF, sizeof(int));
これにより、常に目的の結果が得られます。