web-dev-qa-db-ja.com

boolsを使用したC ++ビットフィールドパッキング

ビットフィールドでテストを行ったところですが、その結果は驚くべきものです。

class test1 {
public:
    bool test_a:1;
    bool test_b:1;
    bool test_c:1;
    bool test_d:1;
    bool test_e:1;
    bool test_f:1;
    bool test_g:1;
    bool test_h:1;
};

class test2 {
public:
    int test_a:1;
    int test_b:1;
    int test_c:1;
    int test_d:1;
    int test_e:1;
    int test_f:1;
    int test_g:1;
    int test_h:1;
};

class test3 {
public:
    int test_a:1;
    bool test_b:1;
    int test_c:1;
    bool test_d:1;
    int test_e:1;
    bool test_f:1;
    int test_g:1;
    bool test_h:1;
};

結果は次のとおりです。-

sizeof(test1) = 1   // This is what I'd expect. 8 bits in a byte
sizeof(test2) = 4   // Reasonable. Maybe padded out to the size of an int.
sizeof(test3) = 16  // What???

これはあなたが期待するものですか、それともコンパイラのバグですか? (Codegear C++ Builder 2007、ところで...)

29
Roddy

コンパイラは、test3のすべてのメンバーを整数サイズの境界に配置しました。ブロックが特定のタイプ(整数ビットフィールドまたはブールビットフィールド)に使用されると、コンパイラは次の境界まで別のタイプのビットフィールドを割り当てません。

バグだとは思えません。それはおそらくあなたのシステムの基礎となるアーキテクチャと関係があります。

編集:

c ++コンパイラは、次のようにメモリ内のビットフィールドを割り当てます。いくつかの連続するビットフィールドメンバー 同じタイプの 順番に割り当てられます。新しいタイプを割り当てる必要があるとすぐに、次の論理メモリブロックの先頭に揃えられます。次の論理ブロックは、プロセッサによって異なります。一部のプロセッサは8ビット境界に整列できますが、他のプロセッサは16ビット境界にしか整列できません。

Test3では、各メンバーは前のメンバーとは異なるタイプであるため、メモリ割り当ては8 *(システムの最小論理ブロックサイズ)になります。あなたの場合、最小ブロックサイズは2バイト(16ビット)なので、test3のサイズは8 * 2 = 16です。

8ビットブロックを割り当てることができるシステムでは、サイズは8になると思います。

27
e.James

その動作の多くは実装(コンパイラ)で定義されているため、ビットフィールドには注意してください。

C++ 03から、9.6ビットフィールド(163ページ):

クラスオブジェクト内のビットフィールドの割り当ては、実装によって定義されます。ビットフィールドの配置は実装によって定義されます。ビットフィールドは、アドレス指定可能なアロケーションユニットにパックされます。 [注:ビットフィールドは、一部のマシンではアロケーションユニットにまたがっており、他のマシンではまたがっていません。ビットフィールドは、一部のマシンでは右から左に割り当てられ、他のマシンでは左から右に割り当てられます。 ]

つまり、これはコンパイラのバグではなく、動作の標準的な定義が不足しているということです。

うわー、それは驚くべきことです。 GCC 4.2.4では、CモードとC++モードの両方で、結果はそれぞれ1、4、および4になります。これが私が使用したC99とC++の両方で動作するテストプログラムです。

#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stdio.h>

struct test1 {
    bool test_a:1;
    bool test_b:1;
    bool test_c:1;
    bool test_d:1;
    bool test_e:1;
    bool test_f:1;
    bool test_g:1;
    bool test_h:1;
};

struct test2 {
    int test_a:1;
    int test_b:1;
    int test_c:1;
    int test_d:1;
    int test_e:1;
    int test_f:1;
    int test_g:1;
    int test_h:1;
};

struct test3 {
    int test_a:1;
    bool test_b:1;
    int test_c:1;
    bool test_d:1;
    int test_e:1;
    bool test_f:1;
    int test_g:1;
    bool test_h:1;
};

int
main()
{
    printf("%zu %zu %zu\n", sizeof (struct test1), sizeof (struct test2),
                            sizeof (struct test3));
    return 0;
}
7

一般的な観察として、1ビットの符号付きintはあまり意味がありません。確かに、おそらく0を格納する方法を理解できますが、それから問題が始まります。

2の補数であっても、1ビットは符号ビットである必要がありますが、使用できるのは1ビットだけです。したがって、それを符号ビットとして割り当てると、実際の値のビットは残りません。 Steve Jessopがコメントで指摘しているように、2の補数を使用すればおそらく-1を表すことができますが、それでも0と-1しか表現できない「整数」データ型はかなり奇妙なものだと思います。

私にとって、このデータ型は意味がありません(または、Steveのコメントを考えると、little)。

使用する unsigned int small : 1;符号なしにするには、値0と1をあいまいさのない方法で格納できます。

4
unwind
#include <iostream>
using namespace std;

bool ary_bool4[10];

struct MyStruct {
    bool a1 :1;
    bool a2 :1;
    bool a3 :1;
    bool a4 :1;
    char b1 :2;
    char b2 :2;
    char b3 :2;
    char b4 :6;
    char c1;
};

int main() {
    cout << "char size:\t" << sizeof(char) << endl;
    cout << "short int size:\t" << sizeof(short int) << endl;
    cout << "default int size:\t" << sizeof(int) << endl;
    cout << "long int size:\t" << sizeof(long int) << endl;
    cout << "long long int size:\t" << sizeof(long long int) << endl;
    cout << "ary_bool4 size:\t" << sizeof(ary_bool4) << endl;
    cout << "MyStruct size:\t" << sizeof(MyStruct) << endl;
    // cout << "long long long int size:\t" << sizeof(long long long int) << endl;
    return 0;
}

char size: 1
short int size: 2
default int size: 4
long int size: 4
long long int size: 8
ary_bool4 size: 10
MyStruct size: 3
1
t1t0

「サミュエル・P・ハービソン、ガイ・L・スティール] C Aリファレンス」より:

問題:

「コンパイラは、ビットフィールドの最大サイズに自由に制約を課し、ビットフィールドが通過できない特定のアドレス指定境界を指定できます。」

標準内で実行できる操作:

「パディングを提供するために、名前のないビットフィールドを構造体に含めることもできます。」

「名前のないビットフィールドに長さ0を指定すると、特別な意味があります。これは、前のビットフィールドが含まれる領域にビットフィールドをこれ以上パックしないことを示します...ここでの領域は、暗黙的に定義されたストレージユニットを意味します。」

これはあなたが期待するものですか、それともコンパイラのバグですか?

したがって、C89、C89、修正I、C99内では、バグではありません。 C++についてはわかりませんが、動作は似ていると思います。

0
bruziuz