在C中的结构中填充
这是一个面试问题。 到目前为止,我曾经认为这些问题纯粹是依赖于编译器的,不应该担心,但是现在,我对此很好奇。
假设你有两个结构:
struct A { int* a; char b; }
而且,
struct B { char a; int* b; }
那么你更喜欢哪一个?为什么? 我的回答是这样的(虽然我有点在黑暗中拍摄),第一个结构应该是首选的,因为编译器为结构分配了一定数量的字大小的空间(这是指针的大小–32字节的4个字节位机器和64位的8个字节)。 所以,对于这两个结构编译器将分配8个字节(假设它是一个32位的机器)。 但是,在第一种情况下,填充会在所有variables之后完成(即在a和b之后)。 所以即使有一些机会,b会得到一些溢出的值,并破坏我的下一个填充字节,但是我的a仍然是安全的。
他似乎并不高兴,并要求在第二个结构的一个缺点。 我没有太多的话要说。 :d
请帮我解答。
我不认为这种结构是有优势的。 这个方程中有一个(!)常数。 结构成员的顺序保证与声明一致。
所以在下面的情况下,第二个结构可能有一个优势,因为它可能有一个较小的尺寸,但不是在你的例子中,因为它们可能会有相同的大小:
struct { char a; int b; char c; } X;
比。
struct { char a; char b; int c; } Y;
关于以下评论的更多解释:
以下所有不是100%,但是结构的常见方式将在32位系统中构build,其中int是32位:
结构X:
| | | | | | | | | | | | | char pad pad pad ---------int---------- char pad pad pad = 12 bytes
结构Y:
| | | | | | | | | char char pad pad ---------int---------- = 8 bytes
当某些机器的值与某个边界alignment时,某些机器可以更高效地访 有些需要数据alignment。
在像SPARC或Intel [86]这样的现代32位机器或者68020以上的任何摩托罗拉芯片上,每个数据通常必须是“自对准”的,从其地址是其倍数types的大小。 因此,32位types必须在32位边界上开始,在16位边界上开始16位types,8位types可以在任何地方开始 ,struct / array / uniontypes具有其限制性最强的成员的alignment。
所以你可以有
struct B { char a; /* 3 bytes of padding ? More ? */ int* b; }
一个简单的规则,最大限度地减less填充“自alignment”的情况下(并没有损害在其他大多数)是通过减less大小为您的结构成员。
我个人认为,与第二个结构相比,第一个结构不是不利的。
在这种情况下,我不能认为第一个结构的劣势,但是有可能提出一些例子,首先把最大的成员放在第一位:
struct A { int* a; short b; A(short num) : b(2*num+1), a(new int[b]) {} // OOPS, `b` is used uninitialized, and a good compiler will warn. // The only way to get `b` initialized before `a` is to declare // it first in the class, or of course we could repeat `2*num+1`. }
我还听说过一个大型结构的复杂情况,在这种情况下,CPU具有访问指针+偏移量的快速寻址模式,对于较小的偏移量(例如,高达8位或其他一些立即值的限制)。 在最快的指令范围内,尽可能多地使用最常用的字段,最好是对大型结构进行微型优化。
CPU甚至可能会针对指针+偏移量和指针+ 4 *偏移量进行快速寻址。 然后假设你有64个char字段和64个int字段:如果你先把char字段放在两个types的所有字段都可以使用最好的指令来寻址,而如果你把int字段放在第一位,那么char字段不是4 -aligned只需要以不同的方式访问,可能是通过将一个常量加载到一个寄存器中而不是立即值,因为它们超出了256字节的限制。
从来没有必要自己做,例如x86允许大的立即值。 这不是任何人通常会考虑的优化,除非他们花费大量的时间凝视组装。
简而言之, 在一般情况下select没有任何优势。 在实践中,唯一的select是重要的是如果结构打包是启用的 ,在struct A
将是更好的select的情况下(因为两个字段将在内存中alignment,而在struct B
, b
字段将位于奇数抵消)。 结构填充意味着在结构内没有填充字节。
但是,这是一个相当不常见的情况:结构打包通常只在特定情况下启用。 这不是大多数计划的关注点。 C标准中的便携式build筑也是不可控制的。
这也是一个猜测,但大多数编译器有一个错位的选项,明确不会添加填充字节。 这就需要(在某些平台上)运行时修正(硬件陷阱)来alignment访问(相应的性能损失)。 如果我记得HPUX属于这个类别。 因此,即使在使用不alignment的编译器选项时,第一个结构的字段仍然是alignment的(因为正如你所说的,填充将会在最后)。