web-dev-qa-db-ja.com

-1を使用してすべてのビットをtrueに設定しても安全ですか?

私はこのパターンがC&C++で多く使用されるのを見てきました。

unsigned int flags = -1;  // all bits are true

これは、これを実現するための優れたポータブルな方法ですか?または0xffffffffまたは~0を使用していますか?

130
hyperlogic
  • unsigned int flags = -1;はポータブルです。
  • unsigned int flags = ~0;は2の補数表現に依存しているため、移植性がありません。
  • unsigned int flags = 0xffffffff;は、32ビット整数を想定しているため、移植性がありません。

C標準で保証されている方法ですべてのビットを設定する場合は、最初のビットを使用します。

48
Dingo

率直に言って、すべてのfffの方が読みやすいと思います。それはアンチパターンであるというコメントについて、すべてのビットが設定/クリアされることを本当に気にするなら、おそらくあなたはおそらくあなたが変数のサイズを気にする状況にあると主張するでしょう、それはブーストのような何かを必要とするでしょう:: uint16_tなど.

25
Doug T.

言及された問題を回避する方法は、単純に行うことです:

unsigned int flags = 0;
flags = ~flags;

ポータブルでポイントまで。

17
hammar

フラグにunsigned intを使用することは、C++のそもそもの良い考えだとは思いません。ビットセットなどはどうですか?

std::numeric_limit<unsigned int>::max()は、0xffffffffは、unsigned intが32ビット整数であると想定しています。

13
Edouard A.
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);

私は、単に正しいというよりも、正しいと自明であると考えています。

11
Adrian McCarthy

-1を符号なしの型に変換すると標準で保証になり、すべて1になります。 ~0Uの型は0であり、unsigned intのようなものを明示的に記述しない限り、より大きな符号なし型のすべてのビットを埋めるわけではないため、~0ULLの使用は一般に悪いです。健全なシステムでは、~0-1と同一である必要がありますが、標準では1の補数と符号/大きさの表現が許可されているため、厳密には移植性がありません。

もちろん、正確に32ビットが必要であることがわかっている場合は0xffffffffを記述することは常に問題ありませんが、-1には、マクロなどの型のサイズがわからなくても、どのコンテキストでも機能するという利点があります複数のタイプで機能するか、タイプのサイズが実装によって異なる場合。タイプがわかっている場合、オールワンを取得する別の安全な方法は、制限マクロUINT_MAXULONG_MAXULLONG_MAXなどです。

個人的に私は常に-1を使用しています。それは常に機能し、あなたはそれについて考える必要はありません。

10
R..

はい。他の回答で述べたように、-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;
5
Diamond Python

インクルードの1つとして#include <limits.h>がある限り、使用するだけです

unsigned int flags = UINT_MAX;

長いビットが必要な場合は、使用できます

unsigned long flags = ULONG_MAX;

これらの値は、符号付き整数の実装方法に関係なく、結果セットのすべての値ビットが1になることが保証されています。

5
Michael Norrish

私は-1をしません。少なくとも直感的には直感的ではありません。符号なしデータに符号付きデータを割り当てることは、物事の自然な順序に違反しているようです。

あなたの状況では、私は常に0xFFFFを使用します。 (もちろん、可変サイズに適切な数のFを使用してください。)

[ところで、実世界のコードで-1トリックが行われることはめったにありません。]

さらに、変数の個々のビットに本当に関心がある場合は、固定幅のuint8_tuint16_tuint32_tタイプを使用することをお勧めします。

3
myron-semack

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ビットを想定しているというすべての回答へのコメントとしてこれを投稿しようと考えましたが、非常に多くの人々がそれに答えたので、別の回答として追加すると思いました。

2
Nathan Fellman

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型に変換されます。

2
supercat

問題の非常に明確な説明については、litbの回答を参照してください。

私の意見の不一致は、非常に厳密に言えば、どちらの場合にも保証がないということです。すべてのビットが設定されているので、「ビット数の2乗未満」の符号なしの値を表さないアーキテクチャは知りませんが、ここに標準が実際に言っているものがあります(3.9.1/7 plus注44):

整数型の表現では、純粋な2進記数法を使用して値を定義する必要があります。 [注44:] 2進数の0と1を使用する整数の位置表現。連続するビットで表される値は加算的で、1で始まり、おそらく2の連続する整数の累乗で乗算されます。最高位。

そのため、ビットの1つがまったく何でもない可能性があります。

2
James Hopkin

実際:はい

理論的に:いいえ。

-1 = 0xFFFFFFFF(またはプラットフォームのintのサイズ)は、2の補数演算でのみ当てはまります。実際には動作しますが、2の補数表現ではなく実際の符号ビットを取得するレガシーマシン(IBMメインフレームなど)があります。提案された〜0ソリューションはどこでも機能するはずです。

1
Drew Hall

他の人が述べたように、-1はすべてのビットを1に設定して符号なしの型に変換する整数を作成する正しい方法です。ただし、C++で最も重要なことは正しい型を使用することです。したがって、あなたの問題に対する正しい答え(あなたが尋ねた質問への答えを含む)はこれです:

std::bitset<32> const flags(-1);

これには、必要なビットの正確な量が常に含まれます。他の回答で述べたのと同じ理由で、すべてのビットを1に設定してstd::bitsetを構築します。

1
David Stone

意味をもう少し明確にする方法と、タイプの繰り返しを避ける方法:

const auto flags = static_cast<unsigned int>(-1);
0
FlorianH

-1は常にすべての使用可能なビットが設定されるため、これは確かに安全ですが、私は〜0が好きです。 -1はunsigned intに対してあまり意味がありません。 0xFF...は、型の幅に依存するため、良くありません。

0
Zifre

符号なしの型に対してすべてのビットを1に割り当てるという事実を活用することは、与えられた型に対して可能な最大値を取ることと同等です。
そして質問の範囲をすべてのunsigned整数型に拡張する:

-1の割り当ては、CとC++の両方の任意のunsigned整数型 (unsigned int、uint8_t、uint16_tなど) 。

別の方法として、C++の場合、次のいずれかを実行できます。

  1. _<limits>_を含め、std::numeric_limits< your_type >::max()を使用します
  2. カスタムテンプレート関数 を記述します(これにより、健全性チェックも可能になります。つまり、宛先タイプが実際に符号なしタイプである場合)

_-1_を割り当てるには常に説明的なコメントが必要になるため、目的はより明確にすることです。

0
Antonio

私は言う:

int x;
memset(&x, 0xFF, sizeof(int));

これにより、常に目的の結果が得られます。

0
Alex