在结构中填充

时间:2014-07-31 08:33:56

标签: c struct

我知道 padding in struct example from this post

 struct A   -->8 bytes
 {
    char c;
    char d;
 //2 padding here
    int i;
 };
 struct B  -->12 bytes
 {
     char c;
 //3 padding here
    int i;
    char d;
 //3 padding here
 };

现在,我不理解以下示例:

 typedef struct {  -->**shouldn't it be 12 bytes**
    int a;
    char *str;
 } TestS;

 TestS s;

int main(int argc, char *argv[]) {

   printf("An int is %lu bytes\n", sizeof( int )); -->4
   printf("A Char * is %lu bytes\n", sizeof( char *)); -->8
   printf("A double is %lu bytes\n", sizeof( double )); -->8

   printf("A struct is %lu bytes\n", sizeof s); -->why 16?

   return 0;

 }

首先我认为它可能对齐8 * N字节(因为我使用 ubuntu-64 ),所以我尝试了更多的结构。

  typedef struct {
   int i;
   char *str;
  } stru_12;


  typedef struct {
    int i;
    char *str;
    char c;
  } stru_13;

 typedef struct {
    int i;
    char str[7];
 } stru_11;

 typedef struct {
   char *str;
   double d;
 } stru_16;

  stru_12 test12;
  stru_13 test13;
  stru_11 test11;
  stru_16 test16;

int main (int argc, char *argv[]) {
    printf("A test12 is %lu bytes, address is %p\n", sizeof test12, &test12);
    printf("A test13 is %lu bytes, address is %p\n", sizeof test13, &test13);
    printf("A test11 is %lu bytes, address is %p\n", sizeof test11, &test11);
    printf("A test16 is %lu bytes, address is %p\n", sizeof test16, &test16);
}
  

结果:

     

test12是16字节,地址是0x601060

     

test13是24字节,地址是0x601090

     

test11是12个字节,地址是0x601080

     

test16是16个字节,地址是0x601070

很抱歉这么久。

我的问题是:

  • 为什么test12(int + char *)是16个字节而test13(int + char * + char)是24?(似乎 8 * N 是有利的,但是12个字节是允许的)

  • 为什么结构地址的差异是16个寻址单位(更多填充?)?

供您使用:

  

cache_alignment:64

     

地址大小:36位物理,48位虚拟

     

Ubuntu 14.04.1 LTS x86_64

2 个答案:

答案 0 :(得分:4)

第二个问题是实现定义的(实际上,第一个问题也是如此,但我会告诉你为什么你要获得你所获得的间距)。您的平台显然是64位,因此您的数据指针同样是(64位)。有了这个,我们就看看结构了。


<强> stru_12

typedef struct 
{
   int i;
   char *str;
} stru_12;

这是对齐的,因此str始终落在8字节边界上,包括在连续序列(数组)中。为此,在istr之间引入了4个字节的填充。

0x0000 i    - length=4
0x0004 pad  - length=4
0x0008 ptr  - length=8
======================
Total               16

如果数组在所说的相同(它将会)上开始,那么这些数组中的数组将始终具有ptr。因为在istr之间添加填充也使结构大小为8的倍数,所以除此之外不需要额外的填充。


<强> stru_13

现在,考虑一下如何实现这一点:

typedef struct 
{
    int i;
    char *str;
    char c;
} stru_13;

相同的填充将在istr之间应用于再次将str放置在8字节边界上,但添加c会使事情变得复杂。为了实现指针始终驻留在8字节边界(包括这些结构的序列/数组)的目标,结构需要尾部填充,但是多少?好吧,我希望很明显整体结构大小需要是8的倍数,以确保任何嵌入式指针(也是8的倍数)都正确对齐。在这种情况下,添加了7个字节的尾部填充以使大小为24个字节:

0x0000 i    - length=4
0x0004 pad  - length=4
0x0008 ptr  - length=8
0x0010 c    - length=1
0x0011 pad  - length=7
======================
Total               24

stru_13(part deux)

试试这个。您认为我们以前使用过的相同字段,但订购的字段不同,将会产生以下结果:

typedef struct 
{
    char *str;
    int i;
    char c;
} stru_13;

嗯,我们知道我们希望{8}在8字节边界上str,在4字节边界上需要i,坦率地说,c可能不在乎(总是新娘-maid):

0x0000 ptr  - length=8
0x0008 i    - length=4
0x000c c    - length=1
0x000d pad  - length=3
======================
Total               16

运行它虽然你的测试程序,你会看到它如上所述。它减少到16个字节。我们所做的只是将顺序更改为仍然支持我们要求的空间更友好的布局,并且我们将默认表示减少了8个字节(原始结构的一个第三个​​与先前的布局)。要说取消所有这一切是一件很重要的事情,这是轻描淡写的。

答案 1 :(得分:3)

指针必须正确对齐,CPU才能使用它们。

在C / C ++中,结构必须在数组中工作,因此在这方面填充结构的末尾。

struct A
{
    char a;
    // 7 bytes of padding
    char *p;
    char b;
    // 7 bytes of padding
};

A array[3];  // the last padding is important to do this

在这样的结构中,p必须对齐,以便处理器可以读取指针而不会产生错误(32位INTEL处理器可以设置为不对齐未对齐的数据,但这不是一个错误好主意:它比较慢,而且经常会跳过错误的错误.64位处理器在这个领域有更多的限制。)

因此,由于你是64位,指针是8个字节,指针前面的对齐必须是8的倍数。

类似地,结构的总大小必须是结构中最大类型的倍数,这里它是8,所以它最后填充到接下来的8个字节。

实际上只有两种情况你应该担心:(1)创建一个要保存在文件中的结构,(2)创建一个你将分配非常大的结构。在所有其他情况下,不要担心它。