2012-04-21 59 views
6

Visual C++同时提供编译器开关(/Zp)和pack编译指示以影响结构成员的对齐。但是,我似乎对他们的工作方式有一些误解。Visual C++中的结构比对

根据MSDN,对于给定的对准值n,

一个构件的对准将是一个边界要么是n的 倍数或构件的大小的倍数,上取是 更小。

我们假设一个8字节的包值(这是默认值)。在一个结构中,我认为任何大小小于8字节的成员的偏移量都是它自己大小的倍数。大小为8个字节或更多的任何成员将位于8个字节的倍数的偏移处。

现在采取以下程序:

#include <tchar.h> 

#pragma pack(8) 

struct Foo { 
    int i1; 
    int i2; 
    char c; 
}; 

struct Bar { 
    char c; 
    Foo foo; 
}; 

int _tmain(int argc, _TCHAR* argv[]) { 
    int fooSize = sizeof(Foo); // yields 12 
    Bar bar; 
    int fooOffset = ((int) &bar.foo) - ((int) &bar); // yields 4 

    return 0; 
} 

Foo结构是在大小为12个字节。因此,在Bar之内,我预计Foo成员的偏移量为8(8的倍数),而实际偏移量为4。为什么?

另外,Foo确实只有4 + 4 + 1 = 9个字节的数据。编译器自动在最后添加填充字节。但是,再次,给定一个8字节的对齐值,它不应该填充到8的倍数而不是4吗?

任何澄清赞赏!

+0

你确定你的'int'只有4个字节吗?你在运行什么机器? – 2012-04-21 10:14:17

+0

@Tony:这是一个32位的应用程序。如果一个int是8个字节而不是4个,那么带有其中两个的Foo不可能只有12个字节。:-) – 2012-04-22 12:00:06

回答

7

你摘录解释这一点,“取其较小者。”在32位平台上,int是4个字节。 4小于8.所以它有一个4字节的对齐方式。

pack编译指示导致东西被打包,不解压缩。除非它有理由,否则它不会放置。

+0

等一下。我有一个Foo成员的Bar结构。所以我认为“成员的大小”是指Foo的大小,即12。你是说编译器实际上是在寻找/ inside/Foo和(可能是递归地)寻找/ primitive/type的最大成员?如果是这样,这种行为是否有一些(在线)参考? – 2012-04-21 10:37:29

+1

编译器知道每个类型的对齐要求。 “Foo”的对齐要求是4.它不是最大的类型,而是外部结构的要求,这通常是任何包含类型的最大对齐要求,但并非总是如此。 (它不能是完整类型的大小,否则,后跟9,000字节结构的整数将会有近9,000个字节的填充。这只是愚蠢的.MSDN页面正在讨论简单类型,其对齐要求等于大小)。 – 2012-04-21 10:56:08

+0

谢谢,这将解释行为。 - 关于你的论点,它不能是完整的结构尺寸:我认为这正是“较小者”规则的原因。你的9,000字节的结构,大于8字节,将被对齐到8的倍数,因此只浪费4个字节而不是8,996。所以对我来说,MSDN算法实际上是有意义的。太糟糕了,它似乎只有一半的事实! – 2012-04-21 11:56:11

1

当你的报价说 - 要测试你需要8种或更多个字节的数据类型为8字节对齐。以下是一些具有显式大小类型的示例。另外,将小元素放在末尾不会显示填充,因为它可以从结构的末端丢弃。

#include <stdio.h> 
int 
main(int argc, char *argv[]) 
{ 
    struct S { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
#pragma pack(push,1) 
    struct T { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
#pragma pack(pop) 
#pragma pack(push,8) 
    struct U { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
    struct B { 
     __int8 c; 
     struct U s; 
    }; 
#pragma pack(pop) 

    printf("S %d T %d U %d B %d\n", 
      sizeof(struct S), sizeof(struct T), 
      sizeof(struct U), sizeof(struct B)); 
    return 0; 
} 

与2010年VC编译如下:

C:\src>cl -nologo -W3 -Od packing.c && packing.exe 
packing.c 
S 24 T 17 U 24 B 32 
4

记住为什么在第一位置对准事宜。它可以让CPU快速读取内存,而不必复用字节。 cpu nevers只读一个结构体,它只访问它的成员。所以Foo结构是12字节这一事实并不重要。只有其成员的一致性很重要。鉴于没有Foo成员的对齐要求大于4,Bar.foo成员只需要对齐到4.

Foo是12字节而不是9可以使用解释。编译器将3个字节的填充添加到末尾,以便Foo数组仍然具有正确对齐每个数组元素的成员。