このパターンがCおよびC++で多く使用されているのを見てきました。
unsigned int flags = -1; // all bits are true
これは、これを達成するための優れたポータブルな方法ですか?それとも使用しています0xffffffff
か~0
?
このパターンがCおよびC++で多く使用されているのを見てきました。
unsigned int flags = -1; // all bits are true
これは、これを達成するための優れたポータブルな方法ですか?それとも使用しています0xffffffff
か~0
?
それは最も簡単なものなので、あなたが示したとおりにそれを行うことをお勧めします。初期化は、実際の符号表現に-1
関係なく常に~
機能しますが、正しいオペランドタイプが必要になるため、驚くべき動作をすることがあります。unsigned
そうして初めて、タイプの最も高い値を取得できます。
考えられる驚きの例として、これを考えてみましょう。
unsigned long a = ~0u;
すべてのビット1を含むパターンをに格納する必要はありませんa
。ただし、最初に、のすべてのビット1でパターンを作成し、unsigned int
次にそれをに割り当てa
ます。より多くのビットがある場合に起こることunsigned long
は、それらのすべてが1であるとは限らないということです。
そして、これを考えてみましょう。これは、2以外の補数表現では失敗します。
unsigned int a = ~0; // Should have done ~0u !
その理由は、~0
すべてのビットを反転する必要があるためです。これを反転すると-1
、2の補数マシン(必要な値です!)では生成されますが、別の表現では生成されません。-1
1の補数のマシンでは、ゼロになります。したがって、1の補数マシンでは、上記はa
ゼロに初期化されます。
理解しておくべきことは、ビットではなく値がすべてだということです。変数は値で初期化されます。初期化子で初期化に使用される変数のビットを変更すると、値はそれらのビットに従って生成されます。a
可能な限り高い値に初期化するために必要な値は、-1
またはUINT_MAX
です。2番目はタイプによって異なります-に使用するa
必要があります。ただし、最初の方法はそのタイプに依存しないため、最も高い値を取得するための優れた方法です。ULONG_MAX
unsigned long
すべてのビットが1であるかどうかについて話しているわけではありません(常にあるとは限りません)。-1
そして、私たちはすべてのビットが1つであるかどうかについて話しているのではありません(もちろん、それは持っています)。~0
しかし、私たちが話しているのは、初期化されたflags
変数の結果が何であるかです。そしてそれのために、すべてのタイプとマシンでのみ動作します。-1
unsigned int flags = -1;
ポータブルです。unsigned int flags = ~0;
2の補数表現に依存しているため、移植性はありません。unsigned int flags = 0xffffffff;
32ビットintを想定しているため、移植性はありません。C規格で保証されている方法ですべてのビットを設定する場合は、最初のビットを使用します。
率直に言って、すべてのfffの方が読みやすいと思います。アンチパターンであるというコメントについては、すべてのビットが設定/クリアされることを本当に気にかけているのであれば、とにかく変数のサイズを気にする状況にあると思います。これは、ブーストのようなものを必要とします。 ::uint16_tなど
上記の問題を回避する方法は、単に次のことを行うことです。
unsigned int flags = 0;
flags = ~flags;
ポータブルで要領を得た。
unsigned int flags = -1; // all bits are true
「これは、これを達成するための優れた[、]ポータブルな方法ですか?」
ポータブル? はい。
良い? このスレッドに示されているすべての混乱から明らかなように、議論の余地があります。仲間のプログラマーが混乱することなくコードを理解できるように十分に明確にすることは、優れたコードを測定するための次元の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++11auto
の場合:これらのいずれかをさらに単純にするために使用できます:
auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);
私は、単に正しいよりも正しく、明白であると考えています。
そもそもC++でフラグにunsignedintを使用するのが良い考えかどうかはわかりません。ビットセットなどはどうですか?
std::numeric_limit<unsigned int>::max()
0xffffffff
unsigned intは32ビット整数であると想定しているため、より優れています。
-1を符号なし型に変換すると、標準によってすべて1になることが保証されます。のようなものを明示的に記述しない限り、は型を持ち、より大きな符号なし型のすべてのビットを埋めることができないため、の使用~0U
は一般的に悪いです。正気のシステムでは、と同じである必要がありますが、標準では1の補数と符号/大きさの表現が許可されているため、厳密に言えば、移植性はありません。0
unsigned int
~0ULL
~0
-1
もちろん、正確に32ビットが必要であることがわかっている場合は、いつでも書き出すこと0xffffffff
ができますが、-1には、複数の型で機能するマクロなど、型のサイズがわからない場合でも、どのような状況でも機能するという利点があります。 、またはタイプのサイズが実装によって異なる場合。タイプがわかっている場合、オールワンを取得するもう1つの安全な方法は、制限マクロ、、、などUINT_MAX
です。ULONG_MAX
ULLONG_MAX
個人的にはいつも-1を使います。それは常に機能し、あなたはそれについて考える必要はありません。
インクルードの1つとして持っている限り、#include <limits.h>
使用する必要があります
unsigned int flags = UINT_MAX;
長いビットの価値が必要な場合は、
unsigned long flags = ULONG_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;
0xFFFF
(または0xFFFFFFFF
など)は読みやすいかもしれませんが、そうでなければ移植可能なコードの移植性を損なう可能性があります。たとえば、特定のビットが設定されているデータ構造内のアイテムの数をカウントするライブラリルーチンについて考えてみます(正確なビットは呼び出し元によって指定されます)。ルーチンは、ビットが何を表すかについて完全に不可知論的である可能性がありますが、それでも「すべてのビットが設定されている」定数を持っている必要があります。このような場合、-1は任意のビットサイズで機能するため、16進定数よりもはるかに優れています。
他の可能性はtypedef
、ビットマスクに値が使用されている場合、〜(bitMaskType)0を使用することです。ビットマスクがたまたま16ビット型しかない場合、その式には16ビットしか設定されません(「int」が32ビットであっても)が、必要なのは16ビットだけなので、1つであれば問題ありません。実際には、型キャストで適切な型を使用します。
ちなみに、longvar &= ~[hex_constant]
16進定数が大きすぎてに収まらない場合、フォームの式には厄介な落とし穴がありますint
が、に収まりunsigned int
ます。anint
が16ビットの場合、longvar &= ~0x4000;
またはlongvar &= ~0x10000
; の1ビットをクリアしますがlongvar
、longvar &= ~0x8000;
ビット15とそれより上のすべてのビットをクリアします。適合する値でint
は、補数演算子が型int
に適用されますが、結果は符号拡張されlong
て上位ビットが設定されます。値が大きすぎるunsigned int
と、タイプに補数演算子が適用されlong
ます。ただし、これらのサイズの間にある値は、補数演算子を型に適用し、符号拡張なしunsigned int
の型に変換されます。long
私は-1のことはしません。それはかなり直感的ではありません(少なくとも私にとっては)。符号付きデータを符号なし変数に割り当てることは、物事の自然な順序に違反しているように見えます。
あなたの状況では、私はいつもを使用します0xFFFF
。(もちろん、可変サイズには適切な数のFを使用してください。)
[ところで、実際のコードで-1トリックが行われることはめったにありません。]
さらに、変数の個々のビットを本当に気にする場合は、固定幅の、、タイプの使用を開始することをお勧めuint8_t
しuint16_t
ますuint32_t
。
IntelのIA-32プロセッサでは、0xFFFFFFFFを64ビットレジスタに書き込んで、期待どおりの結果を得ることができます。これは、IA32e(IA32の64ビット拡張)が32ビットのイミディエートのみをサポートしているためです。64ビット命令では、32ビットイミディエートは64ビットに符号拡張されます。
以下は違法です。
mov rax, 0ffffffffffffffffh
以下は、64個の1をRAXに配置します。
mov rax, 0ffffffffh
完全を期すために、以下ではRAX(別名EAX)の下部に321を配置します。
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つがまったく何でもない可能性があります。
他の人が述べているように、-1は、すべてのビットが1に設定された符号なし型に変換される整数を作成する正しい方法です。ただし、C ++で最も重要なことは、正しい型を使用することです。したがって、あなたの問題に対する正しい答え(あなたが尋ねた質問への答えを含む)はこれです:
std::bitset<32> const flags(-1);
これには、必要なビット数が常に含まれます。std::bitset
他の回答で述べたのと同じ理由で、すべてのビットを1に設定してを構築します。
-1には常に使用可能なすべてのビットが設定されるため、これは確かに安全ですが、私は〜0の方が好きです。-1は、。にはあまり意味がありませんunsigned int
。0xFF
...タイプの幅に依存するので良くありません。
実質的に:はい
理論的には:いいえ。
-1 = 0xFFFFFFFF(またはプラットフォーム上のintのサイズ)は、2の補数演算でのみ真になります。実際には機能しますが、2の補数表現ではなく実際の符号ビットを使用するレガシーマシン(IBMメインフレームなど)があります。提案された〜0ソリューションはどこでも機能するはずです。
私は言う:
int x;
memset(&x, 0xFF, sizeof(int));
これにより、常に望ましい結果が得られます。
符号なし型のすべてのビットを1つに割り当てることは、指定された型の可能な最大値を取得し
、質問の範囲をすべての符号なし整数型に拡張することと同じであるという事実を活用します。
-1の割り当ては、CとC ++の両方の符号なし整数型( unsigned int、uint8_t、uint16_tなど)に対して機能します。
別の方法として、C ++の場合、次のいずれかを実行できます。
<limits>
て使用するstd::numeric_limits< your_type >::max()
-1
割り当てるには常に説明的なコメントが必要になるため、目的はより明確にすることです。
意味をもう少し明確にし、タイプの繰り返しを回避する方法:
const auto flags = static_cast<unsigned int>(-1);
ここでのエイドリアンマッカーシーのアプローチが、標準の適合性、型安全性/明示的な明確さ、および起こりうる曖昧さの低減の間の妥協点に関して、C ++ 11以降、遅くとも最良の解決策である可能性があることを強調する追加の取り組み:
unsigned int flagsPreCpp11 = ~static_cast<unsigned int>(0);
auto flags = ~static_cast<unsigned int>(0); // C++11 initialization
predeclaredflags = ~static_cast<decltype(predeclaredflags)>(0); // C++11 assignment to already declared variable
以下に私の好みを詳しく説明します。Johannesが完全に正しく述べたように、ここでの苛立ちの根本的な原因は、値とビット表現のセマンティクス、および正確に話しているタイプ(割り当てられた値のタイプと可能なコンパイル時の整数定数のタイプ)に関する質問です。符号なし整数値に関するOPの具体的なユースケースでは、すべてのビットのセットを1に明示的に保証する標準の組み込みメカニズムがないため、ここで値のセマンティクスから完全に独立することは不可能であることは明らかです(std :: bitsetは一般的な純粋なビット層参照コンテナですが、問題は一般に符号なし整数に関するものでした)。しかし、ここであいまいさを減らすことができるかもしれません。
「より良い」標準準拠アプローチの比較:
OPの方法:
unsigned int flags = -1;
長所:
短所:
定義を介して最大値を参照する:
unsigned int flags = UINT_MAX;
これにより、-1アプローチの符号付き符号なし遷移の問題が回避されますが、いくつかの新しい問題が発生します。たとえば、ターゲットタイプを符号なしロングに変更する場合は、遅くともここでもう一度2回確認する必要があります。そしてここで、最大値が標準によって1に設定されたすべてのビットにつながるという事実を確認する必要があります(そしてパディングビットが再び懸念されます)。ここでも、ビットのセマンティクスはコードから直接明らかではありません。
最大値をより明示的に参照する:
auto flags = std::numeric_limits<unsigned int>::max();
私の意見では、これはマクロ/定義がなく、関連する型について明示的であるため、より優れた最大値アプローチです。しかし、アプローチタイプ自体に関する他のすべての懸念は残っています。
エイドリアンのアプローチ(そしてなぜ私が思うのか、それはC ++ 11以前とそれ以降に好まれるアプローチです):
unsigned int flagsPreCpp11 = ~static_cast<unsigned int>(0);
auto flagsCpp11 = ~static_cast<unsigned int>(0);
長所:
短所:
たとえば、メンバーに割り当てられている場合、C++11より前のタイプとタイプが一致しない可能性がわずかにあります。
クラスでの宣言:
unsigned long m_flags;
コンストラクターでの初期化:
m_flags(~static_cast<unsigned int>(0))
しかし、C ++ 11以降、decltype + autoの使用は、これらの考えられる問題のほとんどを防ぐために強力です。また、これらのタイプの不一致シナリオの一部(たとえば、インターフェイス境界で)は、-1アプローチでも可能です。
事前に宣言された変数に対する堅牢な最終C++11アプローチ:
m_flags(~static_cast<decltype(m_flags)>(0)) // member initialization case
したがって、ここでのすべてのアプローチの長所と短所の重み付けを完全に把握した上で、遅くともC ++ 11以降、これを推奨されるアプローチとしてお勧めします。
更新:Andrew Henleによるヒントのおかげで、私はその読みやすさに関するステートメントを削除しました。これは主観的すぎるステートメントである可能性があるためです。しかし、static_cast-usageも「確立」されており、定義/マクロ、さらにはstd-lib。
はい、示されている表現は非常に正確です。逆にすると、uはすべてのビットを反転するオペレーターが必要になりますが、この場合、マシン内の整数のサイズを考慮すると、ロジックは非常に単純です。
たとえば、ほとんどのマシンでは、整数は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の答えwhcihは4バイト整数で-1%2 ^ 32
したがって、符号なし整数に対して完全に有効です。
不一致がある場合はplzzレポート