C++ 用布尔值进行位域封装

发布于 2024-07-09 09:28:40 字数 850 浏览 5 评论 0原文

我刚刚对位域进行了测试,结果令我惊讶。

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,顺便说一句...)

I've just done a test with bitfields, and the results are surprising me.

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;
};

The results were:-

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???

Is this what you'd expect, or a compiler bug? (Codegear C++ Builder 2007, btw...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

一张白纸 2024-07-16 09:28:42

您的编译器已将 test3 的所有成员排列在整数大小边界上。 一旦块用于给定类型(整数位字段或布尔位字段),编译器就不会分配任何其他不同类型的位字段,直到下一个边界。

我怀疑这是一个错误。 它可能与您系统的底层架构有关。

编辑:

C++ 编译器将按如下方式在内存中分配位域:将按顺序分配多个连续的相同类型的位域成员。 一旦需要分配新类型,它将与下一个逻辑内存块的开头对齐。 下一个逻辑块将取决于您的处理器。 有些处理器可以对齐到 8 位边界,而其他处理器只能对齐到 16 位边界。

在 test3 中,每个成员的类型都与其之前的成员不同,因此内存分配将为 8 *(系统上的最小逻辑块大小)。 在您的情况下,最小块大小为两个字节(16 位),因此 test3 的大小为 8*2 = 16。在

可以分配 8 位块的系统上,我预计大小为 8。

your compiler has arranged all of the members of test3 on integer size boundaries. Once a block has been used for a given type (integer bit-field, or boolean bit-field), the compiler does not allocate any further bit fields of a different type until the next boundary.

I doubt it is a bug. It probably has something to do with the underlying architecture of your system.

edit:

c++ compilers will allocate bit-fields in memory as follows: several consecutive bit-field members of the same type will be allocated sequentially. As soon as a new type needs to be allocated, it will be aligned with the beginning of the next logical memory block. The next logical block will depend on your processor. Some processors can align to 8-bit boundaries, while others can only align to 16-bit boundaries.

In your test3, each member is of a different type than the one before it, so the memory allocation will be 8 * (the minimum logical block size on your system). In your case, the minimum block size is two bytes (16-bit), so the size of test3 is 8*2 = 16.

On a system that can allocate 8-bit blocks, I would expect the size to be 8.

oО清风挽发oО 2024-07-16 09:28:42

请小心位域,因为它的大部分行为是由实现(编译器)定义的:

来自 C++03,9.6 位域(第 163 页):

a 中位域的分配
类对象是
实现定义的。 的对齐
位字段是实现定义的。
位域被打包成一些
可寻址分配单元。
[注:位域跨式分配
单位在某些机器上而不是在
其他的。 位域已分配
在某些机器上从右到左,
对其他人从左到右。 ]

也就是说,这不是编译器中的错误,而是缺乏其行为方式的标准定义。

Be careful with bitfields as much of its behavior is implementation (compiler) defined:

From C++03, 9.6 Bitfields (pg. 163):

Allocation of bit-fields within a
class object is
implementation-defined. Alignment of
bit-fields is implementation-defined.
Bit-fields are packed into some
addressable allocation unit.
[Note:bit-fields straddle allocation
units on some machines and not on
others. Bit-fields are assigned
right-to-left on some machines,
left-to-right on others. ]

That is, it is not a bug in the compiler but rather lack of a standard definition of how it should behave.

淡写薰衣草的香 2024-07-16 09:28:42

哇,这太令人惊讶了。 在 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;
}

Wow, that's surprising. In GCC 4.2.4, the results are 1, 4, and 4, respectively, both in C and C++ modes. Here's the test program I used that works in both C99 and 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;
}
燕归巢 2024-07-16 09:28:42

作为一般观察,1 位的有符号 int 没有多大意义。 当然,您也许可以弄清楚如何在其中存储 0,但麻烦就开始了。

一位必须是符号位,即使是二进制补码,但您只有一位可以使用。 因此,如果将其分配为符号位,则没有剩余位用于实际值。 正如 Steve Jessop 在评论中指出的那样,如果使用二进制补码,您可能可以表示 -1,但我仍然认为只能表示 0 和 -1 的“整数”数据类型是一件相当奇怪的事情。

对我来说,这种数据类型没有任何意义(或者,根据史蒂夫的评论,没什么)。

使用unsigned intsmall : 1;使其无符号,然后您可以以非二义性的方式存储值0和1。

As a general observation, a signed int of 1 bit doesn't make a lot of sense. Sure, you can probably figure out how to store 0 in it, but then the trouble starts.

One bit must be the sign-bit, even in two's complement, but you only have one bit to play with. So, if you allocate that as the sign-bit, you have no bits left for the actual value. It's true as Steve Jessop points out in a comment that you could probably represent -1 if using two's complement, but I still think that an "integer" datatype that can only represent 0 and -1 is a rather weird thing.

To me, this datatypes makes no (or, given Steve's comment, little) sense.

Use unsigned int small : 1; to make it unsigned, then you can store the values 0 and 1 in a non-ambiguous manner.

深白境迁sunset 2024-07-16 09:28:42
#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
#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
十年不长 2024-07-16 09:28:42

来自“Samuel P. Harbison,Guy L. Steele] CA 参考”:

问题:

“编译器可以自由地对位字段的最大大小施加约束,并指定位字段的某些寻址边界无法跨越。”

可以在标准内完成的操作:

“未命名的位字段也可以包含在结构中以提供填充。”

“为未命名的位字段指定长度为 0 具有特殊含义 - 它表示不应将更多的位字段打包到前一个位字段所在的区域中...这里的区域表示一些隐式定义的存储单元”

这是您所期望的,还是编译器错误?

所以在 C89、带有修正 I 的 C89、C99 中 - 这不是一个错误。 关于 C++ 我不知道,但我认为行为是相似的。

From "Samuel P. Harbison, Guy L. Steele] C A Reference":

The problem:

"Compilers are free to impose constraints on the maximum size of a bit field, and specify certain addressing boundaries that bit field cannot cross."

Manipulations which can be done within standard:

"An unnamed bit field may also be included in a structure to provide padding."

"Specify a length of 0 for unnamed bit field has a special meaning - it indicates that no more bit fields should be packed into the area in which the previous bit field...Area here means some impl. defined storage unit"

Is this what you'd expect, or a compiler bug?

So within C89, C89 with amendment I, C99 - it is not a bug. About C++ I don't know, but I think that the behavior is similar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文