【问题标题】:what is difference between structure padding in c on 32bit and 64bit architecture?32位和64位架构上c中的结构填充有什么区别?
【发布时间】:2016-09-16 09:07:02
【问题描述】:

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

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

【问题讨论】:

  • C 标准不强制任何类型的对齐。它完全取决于平台的 ABI。这不仅仅是 CPU 的问题,例如在某些 32 位架构上,可以使用 64 位对齐,例如用于缓存/总线/RAM效率。这不仅仅是 CPU 的寄存器宽度的问题。换句话说:你的问题太宽泛了。
  • 这取决于您的编译器、编译器设置和目标平台。
  • @Olaf 是否总是在 64 位上使用 32 位架构,何时使用 8 字节而不是 4 字节
  • 1 字节 = 8 位。 32/8=4 但 64/8=8。哪一部分不清楚?

标签: c padding cpu-architecture


【解决方案1】:

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

现在,考虑一个 int:

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 字节 ints 存储在从 4 的倍数开始的地址中,chars 存储在 1 的倍数中,8 字节 doubles 存储在 8 的倍数中,8 字节 long long int 存储在 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 字节 + 双精度为 8 字节 + int 为 4 字节)。

但实际大小为 24 字节

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

struct mystructre arr[4];

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

因此总大小为 24 字节。


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

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

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

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

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


您现在可能已经自己弄清楚了,在 64 位处理器中会发生什么,或者如果 int 的大小不同。

【讨论】:

  • 这对于真实系统上的每个常用 ABI (AFAIK) 都是如此,但 C 标准允许 ABI 结构成员总是打包时没有填充(而不是在结构内自然对齐) .这种设计选择是由性能驱动的,而不是 C 标准中的任何要求,所以你开始展示未对齐的负载如何变慢是件好事。 (现代 x86 CPU 对未对齐的加载/存储具有快速的硬件支持,只要它们不跨越缓存线边界就不会受到惩罚。)此外,1 个周期/访问是吞吐量和延迟的(合理的)简化。
  • 另外值得一提的是,大多数真正的编译器都有指定打包结构的方法,没有填充。例如在 GNU C 中:struct __attribute__((packed)) foo { uint16_t u16; uint32_t u32; uint8_t u8; }; 将是 7 个字节,没有尾随填充。 (在Godbolt compiler explorer 上查看它,我在其中放了一个示例来玩,在具有/不具有packed 属性的同一结构的成员之一上使用 sizeof() 和 offsetof()。
【解决方案2】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2018-08-28
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    • 1970-01-01
    • 2017-06-05
    • 2014-02-05
    • 2016-12-11
    • 1970-01-01
    相关资源
    最近更新 更多