【发布时间】:2010-10-23 21:11:52
【问题描述】:
如果我有一个 C++ 结构,定义一个 64 位数据字,例如..
struct SMyDataWord
{
int Name : 40;
int Colour : 24;
};
: 40 语法是什么意思...是否意味着前 40 位为名称保留,其余 24 位为颜色?
这似乎是它的使用方式,但我以前没有遇到过。
【问题讨论】:
标签: c++ data-structures syntax struct
如果我有一个 C++ 结构,定义一个 64 位数据字,例如..
struct SMyDataWord
{
int Name : 40;
int Colour : 24;
};
: 40 语法是什么意思...是否意味着前 40 位为名称保留,其余 24 位为颜色?
这似乎是它的使用方式,但我以前没有遇到过。
【问题讨论】:
标签: c++ data-structures syntax struct
位域,从 C 中继承。Name 是 40 位宽,Colour 是 24 位宽。因此,您的结构至少有 64 位。在我的系统上,64 位是 8 个字节。
【讨论】:
是的,这是bitfields 的语法。它们通常用于定义映射到硬件寄存器的结构。如果你决定使用它们,有一些事情需要记住,一是你不知道编译器如何在构成字段的实际字节中进行布局、排序和填充,编译器之间可能并且会有所不同(也许使用相同的编译器,但优化设置也不同)。
【讨论】:
这是一个位域定义。
Name 是一个整数,可以准确存储 40 位信息。颜色可以存储 24 位。
这样做通常是为了在经常需要的结构中节省一些空间,或者将代码压缩到 CPU 易于处理的大小(在您的情况下为 64 位。正好适合 64 位机器上的 CPU 寄存器)。
虽然访问位域的代码执行起来会慢一些。
【讨论】:
请记住,几乎所有关于 位域是实现 依赖。例如,是否位 从左到右存储或 从右到左取决于实际 硬件架构。此外, 每个编译器使用不同的成员 对齐模型,这就是为什么尺寸 优化后的 BillingRec 为 12 字节而不是 9。你不能拿 位字段的地址也不能创建 位数组。最后,在大多数 实现位域的使用 产生速度开销。因此,当 你优化你的代码,测量 一定的优化效果和 在您决定使用之前的权衡 它。
【讨论】:
这里sizeof 很好地展示了幕后发生的事情:
#include <iostream>
#include <climits>
struct bc_1 {
int a : 1;
int b : 1;
};
struct bc_2 {
int a : 31;
int b : 1;
};
struct bc_3 {
int a : 32;
int b : 1;
};
struct bc_4 {
int a : 31;
int b : 2;
};
struct bc_5 {
int a : 32;
int b : 32;
};
struct bc_6 {
int a : 40;
int b : 32;
};
struct bc_7 {
int a : 63;
int b : 1;
};
int main(int argc, char * argv[]) {
std::cout << "CHAR_BIT = " << CHAR_BIT;
std::cout << " => sizeof(int) = " << sizeof(int) << std::endl;
std::cout << "1, 1: " << sizeof(struct bc_1) << std::endl;
std::cout << "31, 1: " << sizeof(struct bc_2) << std::endl;
std::cout << "32, 1: " << sizeof(struct bc_3) << std::endl;
std::cout << "31, 2: " << sizeof(struct bc_4) << std::endl;
std::cout << "32, 32: " << sizeof(struct bc_5) << std::endl;
std::cout << "40, 32: " << sizeof(struct bc_6) << std::endl;
std::cout << "63, 1: " << sizeof(struct bc_7) << std::endl;
}
接下来的内容取决于您的编译器和操作系统,也可能取决于您的硬件。在带有 gcc-7 的 macOS 上(CHAR_BIT = 8,32 位 int(即 64 位 long 的一半)有 sizeof(int) = 4)这是我看到的输出:
CHAR_BIT = 8 => sizeof(int) = 4
1, 1: 4
31, 1: 4
32, 1: 8
31, 2: 8
32, 32: 8
40, 32: 12
63, 1: 8
这告诉我们几件事:如果int 类型的两个字段都适合单个int(即上面示例中的32 位),则编译器只分配一个int 的内存价值(@ 987654331@ 和 bc_2)。曾经,单个int 不能再保存位域,我们添加第二个(bc_3 和bc_4)。请注意,bc_5 已满负荷。
有趣的是,我们可以“选择”比允许更多的位。见bc_6。这里 g++-7 给出警告:
bitfields.cpp::30:13: warning: width of 'bc_6::a' exceeds its type
int a : 40;
^~
注意:clang++ 更详细地解释了这一点
bitfields.cpp:30:9: warning: width of bit-field 'a' (40 bits) exceeds the width of its type; value will be truncated to 32 bits [-Wbitfield-width]
int a : 40;
^
然而,在后台,编译器似乎分配了另一个int 的内存。或者至少,它确定了正确的大小。我猜编译器警告我们不要以int a = bc_6::a 访问此内存(我敢打赌int a 将只有字段bc_6::a 的前32 位...)。 bc_7 证实了这一点,其总大小是两个 ints 的大小,但第一个字段涵盖了大部分。
最后将上面示例中的int 替换为long 的行为符合预期:
CHAR_BIT = 8 => sizeof(long) = 8
1, 1: 8
31, 1: 8
32, 1: 8
31, 2: 8
32, 32: 8
40, 32: 16
63, 1: 8
【讨论】: