sizeof 函数如何在有和没有位字段的结构上工作? (填充)
How works the sizeof function on a struct with and without bit field? (padding)
我在 C++ 中测试带有位字段的结构的行为,但我遇到了一些令人困惑的事情。我的操作系统是 Windows 10 x64。
我使用的代码如下:
struct BitFieldTest
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
unsigned char counter1 : 4;
unsigned int counter2 : 4;
};
struct NormalFieldTest
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
unsigned int counter2;
};
struct NormalFieldTest2
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
};
int main()
{
std::cout << "Size of bool: " << sizeof(bool) << std::endl;
std::cout << "Size of unsigned char: " << sizeof(unsigned char) << std::endl;
std::cout << "Size of unsigned int: " << sizeof(unsigned int) << std::endl;
std::cout << "Size of struct with bit field: " << sizeof(BitFieldTest) << std::endl;
std::cout << "Size of struct without bit field: " << sizeof(NormalFieldTest) << std::endl;
std::cout << "Size of struct without bit field: " << sizeof(NormalFieldTest2) << std::endl;
return 0;
}
输出为:
Size of bool: 1
Size of unsigned char: 1
Size of unsigned int: 4
Size of struct with bit field: 8
Size of struct without bit field: 12
Size of struct without bit field 2: 5
我不明白为什么结构的大小是这样的。任何人都可以解释或分享有关该主题的一些链接吗?
只要位域的类型相同,位域只会压缩在一起。所以一个位字段:
struct Test
{
char Test1 : 1;
char Test2 : 1;
char Test3 : 1;
char Test4 : 1;
short Test5 : 4;
}
不会是一个字节长,而是四个。
这是因为两件事——首先,位域不跨越类型边界。 char 中的位域不能与 short 中的位域混合。
其次,short 必须放置在结构开头的双字节边界上以进行对齐。所以编译器将其更改为:
struct Test
{
char Test1 : 1;
char Test2 : 1;
char Test3 : 1;
char Test4 : 1;
char BitPadding : 4;
char AlignmentPadding;
short Test5 : 4;
short BitPadding2 : 12;
}
这使结构达到四个字节长。
现在我们依次检查每个结构。
struct BitFieldTest
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
unsigned char counter1 : 4;
unsigned int counter2 : 4;
};
这里,flag1
、flag2
、flag3
、flag4
都是相同的类型,压缩成一个四位的值存储在一个字节中. counter1
是不同的类型,并且自然对齐为 1,因此它移动到下一个字节边界。 counter2
也是不同的类型,它自然对齐四,所以它移动到下一个四字节边界。如果我们然后总结各个部分的大小和中间填充,我们有:
1 个字节,有四个标志,
1 个字节,带有一个四位计数器,
2 个字节的对齐填充,以及
4 个字节和一个四位计数器。
这加起来为 8 个字节,正是编译器报告的内容。
第二个结构没有位字段,但显示了对齐问题:
struct NormalFieldTest
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
unsigned int counter2;
};
在这里,我们有:
布尔标志 1 的 1 个字节,
1 个字节用于布尔标志 2,
1 个字节用于布尔标志 3,
1 个字节用于布尔标志 4,
1 个字节用于计数器 1,
3 个字节用于对齐填充,以及
counter2 的 4 个字节。
这加起来是 12,这也是编译器报告的值。
第三个结构与第二个非常相似,但缺少内部对齐填充:
struct NormalFieldTest2
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
};
在这里,我们有:
布尔标志 1 的 1 个字节,
1 个字节用于布尔标志 2,
1 个字节用于布尔标志 3,
布尔标志 4 的 1 个字节,以及
counter1 的 1 个字节。
正如编译器所报告的那样,这总共增加了 5 个字节。
作为附加说明,内部类型的自然对齐也可能泄漏到结构本身。考虑以下结构:
struct TrailingAlignment
{
int Field1;
short Field2;
}
这个结构看起来是 6 个字节,但编译后会变成 8 个字节。这是因为这个结构可能会用在一个数组中,如果它的长度是 6 个字节,那么数组中的每个其他项目都会包含一个未对齐 Field1
,导致不支持未对齐访问的系统(如某些 ARM 版本)出现重大问题。为避免这种情况,编译器会在结构末尾插入两个字节,以确保数组中此结构的下一个实例将在四字节边界上对齐,前提是第一个结构已正确对齐。
NormalFieldTest2
不会发生这种情况,因为 bool
和 char
的自然对齐是一个字节,所以无论结构在内存中的什么位置,它总是对齐的.
我在 C++ 中测试带有位字段的结构的行为,但我遇到了一些令人困惑的事情。我的操作系统是 Windows 10 x64。
我使用的代码如下:
struct BitFieldTest
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
unsigned char counter1 : 4;
unsigned int counter2 : 4;
};
struct NormalFieldTest
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
unsigned int counter2;
};
struct NormalFieldTest2
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
};
int main()
{
std::cout << "Size of bool: " << sizeof(bool) << std::endl;
std::cout << "Size of unsigned char: " << sizeof(unsigned char) << std::endl;
std::cout << "Size of unsigned int: " << sizeof(unsigned int) << std::endl;
std::cout << "Size of struct with bit field: " << sizeof(BitFieldTest) << std::endl;
std::cout << "Size of struct without bit field: " << sizeof(NormalFieldTest) << std::endl;
std::cout << "Size of struct without bit field: " << sizeof(NormalFieldTest2) << std::endl;
return 0;
}
输出为:
Size of bool: 1
Size of unsigned char: 1
Size of unsigned int: 4
Size of struct with bit field: 8
Size of struct without bit field: 12
Size of struct without bit field 2: 5
我不明白为什么结构的大小是这样的。任何人都可以解释或分享有关该主题的一些链接吗?
只要位域的类型相同,位域只会压缩在一起。所以一个位字段:
struct Test
{
char Test1 : 1;
char Test2 : 1;
char Test3 : 1;
char Test4 : 1;
short Test5 : 4;
}
不会是一个字节长,而是四个。
这是因为两件事——首先,位域不跨越类型边界。 char 中的位域不能与 short 中的位域混合。
其次,short 必须放置在结构开头的双字节边界上以进行对齐。所以编译器将其更改为:
struct Test
{
char Test1 : 1;
char Test2 : 1;
char Test3 : 1;
char Test4 : 1;
char BitPadding : 4;
char AlignmentPadding;
short Test5 : 4;
short BitPadding2 : 12;
}
这使结构达到四个字节长。
现在我们依次检查每个结构。
struct BitFieldTest
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
unsigned char counter1 : 4;
unsigned int counter2 : 4;
};
这里,flag1
、flag2
、flag3
、flag4
都是相同的类型,压缩成一个四位的值存储在一个字节中. counter1
是不同的类型,并且自然对齐为 1,因此它移动到下一个字节边界。 counter2
也是不同的类型,它自然对齐四,所以它移动到下一个四字节边界。如果我们然后总结各个部分的大小和中间填充,我们有:
1 个字节,有四个标志, 1 个字节,带有一个四位计数器, 2 个字节的对齐填充,以及 4 个字节和一个四位计数器。
这加起来为 8 个字节,正是编译器报告的内容。
第二个结构没有位字段,但显示了对齐问题:
struct NormalFieldTest
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
unsigned int counter2;
};
在这里,我们有:
布尔标志 1 的 1 个字节, 1 个字节用于布尔标志 2, 1 个字节用于布尔标志 3, 1 个字节用于布尔标志 4, 1 个字节用于计数器 1, 3 个字节用于对齐填充,以及 counter2 的 4 个字节。
这加起来是 12,这也是编译器报告的值。
第三个结构与第二个非常相似,但缺少内部对齐填充:
struct NormalFieldTest2
{
bool flag1;
bool flag2;
bool flag3;
bool flag4;
unsigned char counter1;
};
在这里,我们有:
布尔标志 1 的 1 个字节, 1 个字节用于布尔标志 2, 1 个字节用于布尔标志 3, 布尔标志 4 的 1 个字节,以及 counter1 的 1 个字节。
正如编译器所报告的那样,这总共增加了 5 个字节。
作为附加说明,内部类型的自然对齐也可能泄漏到结构本身。考虑以下结构:
struct TrailingAlignment
{
int Field1;
short Field2;
}
这个结构看起来是 6 个字节,但编译后会变成 8 个字节。这是因为这个结构可能会用在一个数组中,如果它的长度是 6 个字节,那么数组中的每个其他项目都会包含一个未对齐 Field1
,导致不支持未对齐访问的系统(如某些 ARM 版本)出现重大问题。为避免这种情况,编译器会在结构末尾插入两个字节,以确保数组中此结构的下一个实例将在四字节边界上对齐,前提是第一个结构已正确对齐。
NormalFieldTest2
不会发生这种情况,因为 bool
和 char
的自然对齐是一个字节,所以无论结构在内存中的什么位置,它总是对齐的.