C 结构中的填充

C 结构中的填充,c,struct,C,Struct,我知道有 现在,我不明白下面的例子: 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

我知道有

现在,我不明白下面的例子:

 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*)是24字节?(似乎8*N是受欢迎的,但允许12字节)

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

供您使用:

缓存对齐:64

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

Ubuntu 14.04.1 LTS x86_64


指针必须正确对齐,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)创建一个将大量分配的结构。在所有其他情况下,不用担心。

第二个问题是实现定义的(事实上,第一个问题也是如此,但我将向您展示为什么要获得不考虑的间距)。您的平台显然是64位的,因此您的数据指针也是64位的。有了这些,我们就可以窥视结构了


stru_12

typedef struct 
{
   int i;
   char *str;
} stru_12;
这是对齐的,因此
str
始终位于8字节边界上,包括在连续序列(数组)中。为此,在
i
str
之间引入了4个字节的填充

0x0000 i    - length=4
0x0004 pad  - length=4
0x0008 ptr  - length=8
======================
Total               16
这些数组的8字节边界上总是有
ptr
,前提是数组从所述边界开始(它将从该边界开始)。由于在
i
str
之间添加了填充,也使结构大小达到了8的倍数,因此除此之外不需要额外的填充


stru_13

现在,考虑一下这也是如何实现的:

typedef struct 
{
    int i;
    char *str;
    char c;
} stru_13;
同样的填充将在
i
str
之间应用,以再次将
str
放置在8字节边界上,但是添加
c
会使事情复杂化。为了实现指针始终位于8字节边界(包括这些结构的序列/数组)上的目标,该结构需要尾部填充,但需要多少?我希望很明显,整体结构大小需要是8的倍数,以确保任何嵌入式指针(也是8的倍数)正确对齐。在这种情况下,将添加七个字节的尾部填充,以使大小达到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(双份)

所以试试这个。您认为我们以前拥有的相同字段,但顺序不同,会产生什么结果:

typedef struct 
{
    char *str;
    int i;
    char c;
} stru_13;
好吧,我们知道我们想要8字节边界上的
str
,4字节边界上的
i
,坦率地说,我们对
c
(总是一个伴娘)毫不关心:


通过你的测试程序运行它,你会看到它像我们上面所说的那样成功。它减少到16字节。我们所做的只是将订单更改为一个空间友好的布局,该布局仍然支持我们的需求,并且我们将默认表示形式减少了8个字节(与先前布局相比是原始结构的三分之一)。要说这是一件重要的事情,要从这一切中去掉,这是一种轻描淡写的说法。

这段代码非常嘈杂。您是否可以删除所有的typedef和变量,改为使用
sizeof(struct stru_12)
等?减少视觉混乱。谢谢你的建议,但我需要地址。任何解?24==8*3。8是校准单元,而不是12。地址之间的差异没有意义,除非它们是同一数组中元素的地址。@Tony,一般来说,每个memeber都应该适当对齐,以便整个struct对象,因为它可以在数组中使用。@Tony:地址相对来说没有意义,所以我不想麻烦它,但如果你想保持原样,那很好。由你决定。(当然还有你的问题!)这是一个很好的答案,但是12字节test11的情况是什么呢?@martin
stru_11
没有指针或双指针,所以8字节的边界就不存在了,但是理想的地址访问
i
(32位
int
)应该把它放在4字节的边界上,并且再次按顺序保持它。为了实现这一点,在结构尾部添加了一个额外的填充。结果是一个12字节的长度,
i
总是落在一个4字节的边界上(当然,假设它从一开始,它会这样做)。在
i
的类型中切换
long
short
,看看会发生什么。@martin还值得注意的是,当您将成员从结构中的最大值到最小值排序时,情况可能会发生很大的变化。尤其值得玩玩。@Tony关键是你不必这么做。编译器正在为您执行此操作。大多数情况下你都很好。如果您需要压缩每页更多的项目,以更好地利用
typedef struct 
{
    char *str;
    int i;
    char c;
} stru_13;
0x0000 ptr  - length=8
0x0008 i    - length=4
0x000c c    - length=1
0x000d pad  - length=3
======================
Total               16