我想知道以下代码究竟在做什么?我知道这与记忆对齐有关但当我要求尺寸(车辆)它打印20但结构的实际尺寸是22.我只需要了解它是如何工作的,谢谢! / p>
struct vehicle {
short wheels:8;
short fuelTank : 6;
short weight;
char license[16];
};
printf("\n%d", sizeof(struct vehicle));
20
答案 0 :(得分:2)
存储器将被分配为(假设存储器字大小为8
位)
struct vehicle {
short wheels:8; // 1 byte
short fuelTank : 6;
// padd 2 bits to make fuelTank of 1 byte.
short weight; // 2 bytes.
char license[16]; // 16 bytes.
};
1 + 1 + 2 + 16 = 20
字节。
答案 1 :(得分:1)
考虑一个字大小为32位的机器。两个第一个字段适合整个16位字,因为它们占用8 + 6 = 14
位。第二个字段,虽然不是位域(没有:<number>
的东西来分配空间)可以适合另一个16位字来完成一个32位字,所以三个第一个字段可以打包成一个32位字(4字节)如果架构允许以16位数量访问存储器。最后,如果你添加16个字符,这将给sizeof
运算符发送到printf的20个字节。
为什么假设sizeof (struct vehicle)
是22个字节?你允许编译器打印它,它说它是20.编译器可以自由填充(或不填充)结构以获得更好的性能。这是一种体系结构依赖性,并且由于您没有说过使用的体系结构和编译器,因此无法进一步发展。
例如,32位intel arch允许在偶数边界填充字而不会造成性能损失,因此这是一个很好的选择,以节省内存。在其他体系结构上,也许不允许使用16位整数,必须填充数据以适应第三个字段(整个结构导致22个字节)
在调整数据大小时,唯一的保证是编译器必须分配足够的空间以便以有效的方式适应所有内容,因此您可以从该声明中假设的唯一内容是它至少占用代表一个空间的最小空间8位的字段,6的其他字段,一个完整的短(我假设一个短的是16位)和16个字符(假设每个字符8位),它最小到8 + 6 + 16 + 16*8 = 158 bits
。
假设我们正在为 D编写编译器。 Knuth MIX机器。正如在他的书 Fundamental Algorithms 中所述,该机器具有64..100字节的未指定字节大小,需要五个来构造一个可寻址字(加上二进制符号)。如果你有一个独立于字节大小的编译器(一个编译任何MIX机器的编译器,没有字节大小的假设),你必须每个字节使用不超过64个可能的值,导致每个字节6位。然后你会假设第二个字段填充一个完整的字节(以及从它所属的字中提取的符号),第一个字段需要两个完整的字节(使用负值的一半值)第三个字段可能在第二个字中,填充三个完整字节(6*3 = 18
)和该单词的符号。接下来的16个字符可以从下一个字开始,总计最多五个完整的字,因此整个结构将有1 + 1 + 4 = 6
个字或30个字节。但是如果你想有效地处理三个有符号的字段,你需要三个字段的三个完整的字(因为每个字段只有一个符号字段),导致7个字或35个字节。
我之所以建议这个例子是因为这个体系结构的特殊特性使得人们可以考虑不久前常见的架构(第一台机器曾经构建过不基于二进制的架构,比如其中一些MIX机器)
您可以尝试打印字段的实际偏移量,以查看结构中的位置并查看编译器填充的位置。
#define OFFSET(Typ, field) ((int)&((Typ *)0)->field)
(注意,编辑)
此宏会将偏移量告诉您int
。将其用作OFFSET(struct vehicle, weight)
或OFFSET(struct vehicle, license[3])
我必须编辑最后一个宏定义,因为它在一些架构上抱怨指针的转换 - &gt; int并不总是可能的(在64位体系结构上,它会丢失一些位)所以最好计算两个指针的差异,这是一个正确的size_t
值,而不是直接从指针转换它。
#define OFFSET(Typ, field) ((char *)&((Typ *)0)->field - (char *)0)