web-dev-qa-db-ja.com

C ++ 14のバイナリリテラルのエンディアンは何ですか?

検索してみましたが、バイナリリテラルとエンディアンについて多くを見つけることができませんでした。バイナリリテラルはリトルエンディアン、ビッグエンディアン、またはその他のもの(ターゲットプラットフォームのマッチングなど)ですか?

例として、0b0111の10進値は何ですか? 7ですか?プラットフォーム固有ですか?他に何かありますか? 編集: 1バイト以内で表されるため、7という不正な値を選択しました。この事実にもかかわらず、質問は十分に答えられました。

いくつかの背景:基本的に、最下位ビットの値が何であるかを理解しようとしています。バイナリリテラルでマスクすることは、良い方法のように思えました...ただし、エンディアンについて何らかの保証がある場合に限ります。

42
Levi Morrison

簡単な答え:1つはありません。紙に書くのと同じように番号を書いてください。

長い答え:エンディアンネスは、実際にそれを取り出そうとしない限り(ポインターのトリックを使用するなど)、コードに直接公開されることはありません。 0b0111は7で、16進数と同じルールです。

int i = 0xAA77;

一部のプラットフォームでは0x77AAを意味するわけではありません。それは、ばかげているからです。欠落している余分な0は、とにかく32ビットintでどこに行きますか?それらは前面にパディングされてから、すべてが0x77AA0000に反転しますか、それとも後に追加されますか?もしそうなら、誰かが何を期待するのか私にはわかりません。

重要なのは、C++はマシンのエンディアンについて何も仮定しないということです*。プリミティブとそれが提供するリテラルを使用してコードを記述した場合、動作はマシン間で同じになります(型システムを回避し始めない限り、あなたがする必要があるかもしれません)。

更新に対処するには:番号はあなたがそれを書き出す方法になります。ビットは並べ替えられません。最上位ビットは左側にあり、最下位ビットは右側にあります。


ここではエンディアンとは何かについて誤解があるようです。エンディアンとは、bytesがメモリ内でどのように順序付けられ、どのように解釈されなければならないかを指します。番号「4172」を付けて「これが四千百七十二だとしたら、エンディアンとは何か」と言ったら、質問が意味をなさないので、本当に答えることはできません。 (左側の最大の数字はビッグエンディアンを意味すると主張する人もいますが、メモリアドレスがないと、エンディアンの問題は答えられないか、関連性がありません)。これは単なる数値であり、解釈するバイトはなく、メモリアドレスもありません。 4バイトの整数表現を想定すると、それに対応するバイトは次のとおりです。

        low address ----> high address
Big endian:    00 00 10 4c
Little endian: 4c 10 00 00

したがって、これらのいずれかが与えられ、「これは4172のコンピューターの内部表現です」と言われると、そのリトルエンディアンかビッグエンディアンかを判断できます。

バイナリリテラル0b0111を考えてみましょう。これらの4ビットは1つのニブルを表し、次のいずれかとして格納できます。

              low ---> high
Big endian:    00 00 00 07
Little endian: 07 00 00 00

ただし、これもハードウェアによって処理されるため、気にする必要はありません。言語は、コンパイラが左から右へ、最上位ビットから最下位ビットへと読み取るように指示します。

エンディアンは個々のビットに関するものではありません。バイトが8ビットであるとすると、0b00000111を渡して、「これはリトルエンディアンですか、それともビッグエンディアンですか?」と言います。繰り返しますが、バイトが1つしかない(アドレスがない)ため、言うことはできません。エンディアンは、バイト内のビットの順序には関係しません。アドレスに関するバイト全体の順序を指します(もちろん、1ビットのバイトがない場合)。

コンピュータが内部で何を使用しているかを気にする必要はありません。 0b0111は、次のようなものを書く手間を省くだけです。

unsigned int mask = 7 // only keep the lowest 3 bits

書くことによって

unsigned int mask = 0b0111;

番号の重要性を説明するコメントは必要ありません。


* c ++ 20では、 std :: endian を使用してエンディアンを確認できます。

71
Ryan Haining

2進数を含むすべての整数リテラルは、通常の数値の読み取りと同じ方法で解釈されます(左端の桁が最も重要です)。

C++標準は、使用している特定の環境を気にすることなく、リテラルの同じ解釈を保証します。したがって、このコンテキストではエンディアンを気にする必要はありません。 。

0b0111の例は常に7に等しくなります。

C++標準では、数値リテラルに関してエンディアンの用語を使用していません。むしろ、リテラルには一貫した解釈があり、その解釈はあなたが期待するものであると単純に説明しています。

C++標準-整数リテラル-2.14.2-段落1

整数リテラルは、ピリオドまたは指数部分を持たない一連の数字であり、オプションで一重引用符を区切り、その値を決定するときに無視されます。整数リテラルには、そのベースを指定する接頭辞と、そのタイプを指定する接尾辞が含まれる場合があります。 数字のシーケンスの字句的に最初の数字が最も重要です。 2進整数リテラル(基数2)は0bまたは0Bで始まり、2進数のシーケンスで構成されます。8進数整数リテラル(基数8)は、数字0で始まり、次のシーケンスで構成されます。 8進数。 10進整数リテラル(基数10)は、0以外の数字で始まり、一連の10進数字で構成されます。 16進整数リテラル(基数16)は、0xまたは0Xで始まり、10進数字と10進値10から15の文字aからfおよびAからFを含む16進数字のシーケンスで構成されます。 [例:12という数字は、12、014、0XC、または0b1100と書くことができます。リテラル1048576、1’048’576、0X100000、0x10’0000、および0’004’000’000はすべて同じ値です。 —終了例]

ウィキペディアはエンディアンとは何かを説明し、例として私たちの記数法を使用してbig-endian を理解します

エンディアンおよびエンディアンという用語は、データワードを構成するバイトがコンピュータメモリに格納されているときにそれらのバイトを解釈するために使用される規則を指します。

ビッグエンディアンシステムは、ワードの最上位バイトを最小アドレスに格納し、最下位バイトを最大アドレスに格納します(「最上位」も参照)ビット)。対照的に、リトルエンディアンシステムは、最下位バイトを最小アドレスに格納します。

エンディアンの例は、10進数が場所と値の表記でどのように読み書きされるかを考えることです。数字が左から右に書き込まれる書記体系を想定すると、左端の位置は使用されるメモリの最小アドレスに類似しており、右端の位置が最大です。たとえば、123という数字は1 2 3と書かれ、100の位が一番左になります。 この数字を読む人は誰でも、左端の桁が最大の場所の値を持っていることも知っています。これは、日常生活で行われているビッグエンディアンの慣習の例です。

この文脈では、整数リテラルの数字を「単語のバイト」と見なし、単語自体をリテラルと見なします。また、リテラルの左端の文字は、アドレスが最小であると見なされます。

リテラル1234の場合、1、2、3、および4の数字は「単語のバイト」であり、1234は「単語」です。バイナリリテラル0b0111の場合、0、1、1、1の数字は「単語のバイト」であり、単語は0111です。

この考慮事項により、C++言語のコンテキストでエンディアンを理解することができ、整数リテラルが「ビッグエンディアン」に類似していることがわかります。

40
Michael Gazonda

ソースコードで記述されたエンディアンとオブジェクトコードで表されたエンディアンの区別がありません。それぞれの答えは驚くべきことではありません。ソースコードリテラルは、人間がそれらを読み取る方法であるため、ビッグエンディアンです。オブジェクトコードでは、それらは書き込まれますが、ターゲットはそれらを読み取ります。

バイトは定義上、メモリアクセスの最小単位であるため、バイト内のビットの内部表現にエンディアンを帰することさえできないと思います。これは、より大きな数のエンディアンを発見する唯一の方法です(意図的かどうかは関係ありません)。驚いたことに)はストレージからピースごとにアクセスすることであり、バイトは定義上、アクセス可能な最小のストレージユニットです。

9
jthill

C/C++言語は、マルチバイト整数のエンディアンを気にしません。 C/C++コンパイラはそうします。コンパイラはソースコードを解析し、特定のターゲットプラットフォーム用のマシンコードを生成します。一般に、コンパイラは整数を格納するのと同じ方法で整数リテラルを格納します。ターゲットCPUの命令が、メモリへの読み取りと書き込みを直接サポートするようにします。

コンパイラはターゲットプラットフォーム間の違いを処理するので、そうする必要はありません。

エンディアンについて心配する必要があるのは、バイト順序が異なる他のシステムとバイナリ値を共有している場合のみです。次に、バイナリデータをバイトごとに読み取り、メモリ内のバイトを正しい順序で配置します。コードが実行されているシステム。

7
Theron W Genaux

1枚の写真が1000語を超えることもあります。

source vs. memory endianness

3
Zoltan Tirinda

エンディアンは実装によって定義されます。この標準では、すべてのオブジェクトがcharと_unsigned char_の配列としてオブジェクト表現を持っていることが保証されています。これは、memcpy()またはmemcmp()を呼び出すことで操作できます。 C++ 17では、任意のオブジェクトタイプへのポインタまたは参照(voidへのポインタ、関数へのポインタ、またはnullptrではない)を_reinterpret_cast_することは合法です。 char、_unsigned char_、または_std::byte_へのポインタ。これらは任意のオブジェクトタイプの有効なエイリアスです。

「エンディアン」について話すときに人々が意味するのは、そのオブジェクト表現のバイトの順序です。たとえば、unsigned char int_bytes[sizeof(int)] = {1};と_int i;_を宣言すると、memcpy( &i, int_bytes, sizeof(i));は0x01、0x01000000、0x0100、0x0100000000000000、または他の何かを取得しますか?答えは:はい。これらの各結果を生成する実際の実装があり、それらはすべて標準に準拠しています。これは、コンパイラがCPUのネイティブ形式を使用できるようにするためです。

これは、プログラムがインターネットを介してデータを送受信する必要がある場合に最も頻繁に発生します。すべての標準では、x86などのリトルエンディアンCPUでデータをビッグエンディアン順に送信する必要があると定義されています。したがって、一部のネットワークライブラリでは、特定の引数と構造体のフィールドをホストまたはネットワークのバイト順に格納するかどうかを指定します。

この言語では、オブジェクト表現のビットを任意にいじることで足を撃ち抜くことができますが、トラップ表現になる可能性があり、後で使用しようとすると未定義の動作が発生する可能性があります。 (これは、たとえば、仮想関数テーブルを書き換えて任意のコードを挿入することを意味します。)_<type_traits>_ヘッダーには、オブジェクト表現で安全に実行できるかどうかをテストするためのテンプレートがいくつかあります。タイプが_is_trivially_copyable_の場合、memcpy( &dest, &src, sizeof(dest) )を使用して同じタイプのオブジェクトを別のオブジェクトにコピーできます。 _is_trivially_move_constructible_の場合、正しく整列された初期化されていないメモリにコピーを作成できます。同じタイプの2つのオブジェクトがmemcmp( &a, &b, sizeof(a) )と同一であるかどうかをテストし、タイプが_has_unique_object_representations_の場合、オブジェクト表現のバイトにハッシュ関数を適用することでオブジェクトを正しくハッシュできます。整数型にはトラップ表現などはありません。ただし、ほとんどの場合、エンディアンが重要なオブジェクト表現で操作を行う場合は、コンパイラに、自分が何をしているかを知っていると想定するように指示しているため、コードは移植できません。

他の人が言及しているように、バイナリリテラルは、10進数、8進数、または16進数のリテラルのように、最上位桁を最初に記述されます。これはエンディアンとは異なり、インターネットから読み込まれたTCPヘッダー)からポート番号でntohs()を呼び出す必要があるかどうかには影響しません。

0
Davislor