这句话来自 Kernighan & Ritchie所著《C程序设计语言》第二版第6.9节第150页,涉及IT技术相关内容。该句话解释了在C语言中如何使用字段对齐,表示字段是否可以跨越单词边界需要根据具体实现而定,同时未命名的字段可以用于填充。使用宽度为0的特殊字段可以强制在下一个单词边界处对齐。
"一个字段是否可以跨越词边界是由实现定义的。
考虑两个内存单元,其中字长为32位:
[31] [30] [29] ... [2] [1] [0] | [31] [30] [29] ... [2] [1] [0]
struct X
{
int a : 30;
int b : 4;
};
编译器可能会选择将字段b分成两部分,使其位于不同的字中,也可能留下空隙,使得整个b都在第二个字中:
[31] [30] [29] ... [2] [1] [0] | [31] [30] [29] [28] ... [2] [1] [0]
a--------------------a b-----------------b
OR
a--------------------a GAP b-----------------b
为什么会留下一个间隙?因为这样当它想要读或写b时,它只需要在内存中处理一个单词 - 这通常更快、更简单,需要更少的CPU指令。
字段不需要命名;未命名字段(只有冒号和宽度)用于填充。
如果我们改变之前的结构体,我们可以明确地要求一个间隙:
struct X
{
int a : 30;
int : 2; // unnamed field
int b : 4;
};
这意味着编译器可以计算出部分填充的字中剩余了多少位,并跳到下一个字的开头。就像我们通过添加上面的2位字段(给定a为30位和字长为32)确保b从新的字开始一样,我们也可以...特殊宽度0可以用于强制对齐到下一个字边界。”
struct X
{
int a : 30;
int : 0; // unnamed field
int b : 4;
};
...编译器将为我们计算出2。这样,如果我们将a更改为其他大小,或者最终编译为64位字大小,编译器将自动调整适当的行为,而无需手动更正未命名字段。
基本上,如果一个地址是“字对齐”的,处理器可以更快地执行操作。一个字通常是32位或4字节。
典型处理器是“字”对齐的,这意味着它们可以在一次操作中检索整个内存“字”。当一个值跨越多个值时,处理器必须执行多个操作才能获取相同的数据。有时,这是不可避免的,例如如果您使用一个“双字”,但如果您有一个单个字跨越一个字边界,CPU将不得不执行2个操作来检索单个字的数据。
一个字对齐值的例子是0x10000004、0x10000008。由于一个字是4字节,地址必须是4的倍数。一个非字对齐值是0x10000003。
对于程序员来说,所有操作都会按预期工作,但在底层,CPU必须执行1个内存操作来读取或写入到0x10000004,而它必须执行2个内存操作来读取或写入到0x10000003,因为它跨越了一个字边界。
关于您最初的问题,基本上是说根据您使用的编译器,编译器可能会或可能不会对您的字段进行字对齐。这是大小与速度的例子,如果您不进行字对齐,可以打包更多数据,但如上所示,它将变得更慢。首先,这主要与内存“对齐”有关。编译器通常会将变量或字段对齐到字边界上,32位平台上一个字是32位。这意味着两个布尔值将成为不同单词中的第一个字节,而不是连续的两个字节。
位域可以强制在内存中进行布局:如果其值范围为0-7,则可以确保特定字段仅使用3位。
字段可以没有名称。如果您不打算使用它,则不需要命名字段。这可以用于强制特定的布局。
如果使用:0
,它将自动对齐到下一个字边界。
一般来说,除非以某种方式调整性能,否则不需要此行为。