c 在 32 位和 64 位架构上的结构填充有什么区别?

what is difference between structure padding in c on 32bit and 64bit architecture?

我从 http://fresh2refresh.com/c-programming/c-structure-padding/ 读到内存在 32 位处理器中排列为一组 4 个字节,在 64 位处理器中排列为 8 个字节,但没有阐明这两者之间的区别。

struct structure2 
    {
           int id1;
           char name;
           int id2;
           char c;
           float percentage;                      
    };

本网站并未准确说明使用的是哪种 64 位平台,但似乎假定 ILP64(intlong 和指针为 64 位)平台的长度为-对齐的整数。这意味着 int 在 32 位处理器上是四个字节,在 64 位处理器上是八个字节,并且每个都必须按其自身长度的倍数对齐。

结果是 nameid2 之间的填充长度发生变化(填充是保持 id2 对齐所必需的)。

在 32 位平台上,将有三个字节的填充;在 64 位平台上,将有七个。

cpercentage 之间的填充可能不会改变,因为浮点变量的大小不受处理器位数的影响。

对于32位处理器(更具体地说,它是在谈论数据总线的大小而不是寄存器的大小),意味着一次读取和处理32位(4字节)的数据。

现在,考虑一个整数:

int a=10; //assuming 4 bytes

00000000 000000000 00000000 00001010

假设小端架构,它将存储为:

------------------------------------------------------------------------
| 00001010  |  00000000  |  00000000   |  00000000   |  <something_else>   
-------------------------------------------------------------------------
  1st byte     2nd byte     3rd byte      4th byte
\--------------------------------------------------/
                         |
               4 bytes processed together

在这种情况下,当处理器读取要处理的数据时,它可以一次处理整个整数(所有 4 个字节一起)(更严格地说在 1 个机器周期内)

然而,请考虑存储相同整数的情况,

------------------------------------------------------------------------
|<something_else>| 00001010  |  00000000  |  00000000   |  00000000   |     
-------------------------------------------------------------------------
  1st byte     2nd byte     3rd byte      4th byte
\------------------------------------------------------/
                         |
               4 bytes processed together

在这种情况下,处理器需要 2 个机器周期来读取整数。


大多数架构总是尽量减少 CPU 周期。 因此,许多编译器首选内存中的第一种排列,从而强制执行对齐要求(填充)。 所以4字节int存放的是4的倍数开始的地址,chars存放的是1的倍数,8字节的doubles存放的是8的倍数,8字节的[=20] =] 8 的倍数等等...

现在考虑你的结构

struct structure2 
{
       int id1;   //assuming 4 byte int
       char name;  // 1byte
       int id2;    //4 byte
       char c;     // 1 byte
       float percentage;    //assuming 4 byte float                  
};

id1 将存储在内存中的某个地址(4 的起始倍数)中并占用 4 个字节。

name 将占用下一个字节。

现在如果id2被存储在下一个字节,它会打破上面的对齐规则。因此它将留下 3 个字节的填充 并从地址开始存储,该地址是 4 的下一个倍数,将占用 4 个字节。

对于 c 再次发生与 name 同样的事情。它需要接下来的 1 个字节并保留 3 个字节的填充。

最后 百分比 存储在接下来的 4 个字节中。

因此结构的总大小变为 20 字节


更复杂的情况可以说

struct mystructure
{
   char        a; //1 byte char
   double      b; // 8 byte double
   int         c; // 4 byte int
}

这里乍一看可能会说大小是 20 字节(char 1 字节 + 7 字节填充 + double 8 字节 + int 4 字节)。

但是实际大小是 24 字节

假设有人声明了一个这种结构的数组

struct mystructre arr[4];

这里(假设20字节结构)虽然arr[0]是正确对齐的,但是如果仔细检查就会发现arr[1].b是错位的。所以在结构的末尾添加了 4 个字节的额外填充,使结构大小为其对齐的倍数。(每个结构也有自己的对齐要求)。

因此总大小为 24 字节。


整型、长整型等的大小由编译器决定。编译器通常会处理处理器架构,但它可能会选择不这样做。

同样,是否使用padding是由编译器决定的。不填充被称为packing。一些编译器有允许打包的明确选项。

在 GCC(GNU C 编译器)中你可以用 __attribute__((__packed__)) 来完成,所以在下面的代码中

struct __attribute__((__packed__)) mystructure2 
{
 char a;
 int b;
 char c;
};

mystructure2 的大小为 6 个字节,因为 明确请求打包结构 。此结构将处理得更慢


你现在大概可以自己弄清楚,在 64 位处理器中会发生什么,或者如果 int 的大小不同。