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(int
、long
和指针为 64 位)平台的长度为-对齐的整数。这意味着 int
在 32 位处理器上是四个字节,在 64 位处理器上是八个字节,并且每个都必须按其自身长度的倍数对齐。
结果是 name
和 id2
之间的填充长度发生变化(填充是保持 id2
对齐所必需的)。
在 32 位平台上,将有三个字节的填充;在 64 位平台上,将有七个。
c
和 percentage
之间的填充可能不会改变,因为浮点变量的大小不受处理器位数的影响。
对于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 的大小不同。
我从 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(int
、long
和指针为 64 位)平台的长度为-对齐的整数。这意味着 int
在 32 位处理器上是四个字节,在 64 位处理器上是八个字节,并且每个都必须按其自身长度的倍数对齐。
结果是 name
和 id2
之间的填充长度发生变化(填充是保持 id2
对齐所必需的)。
在 32 位平台上,将有三个字节的填充;在 64 位平台上,将有七个。
c
和 percentage
之间的填充可能不会改变,因为浮点变量的大小不受处理器位数的影响。
对于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 的大小不同。