考虑:

struct mystruct_A
{
   char a;
   int b;
   char c;
} x;

struct mystruct_B
{
   int b;
   char a;
} y;

结构尺寸分别为12和8。

这些结构是填充的还是包装的?

什么时候进行填充或包装?


当前回答

只有当你告诉编译器显式地对结构进行打包时,才会进行结构打包。你看到的是填充。您的32位系统正在填充每个字段以字对齐。如果您告诉编译器打包结构,它们将分别为6和5字节。但是不要这样做。它不可移植,使编译器生成的代码更慢(有时甚至有bug)。

其他回答

变量存储在可以被其对齐方式(通常是大小)整除的任何地址上。所以,填充/填充不仅仅是为了结构。实际上,所有数据都有自己的对齐要求:

int main(void) {
    // We assume the `c` is stored as first byte of machine word
    // as a convenience! If the `c` was stored as a last byte of previous
    // word, there is no need to pad bytes before variable `i`
    // because `i` is automatically aligned in a new word.

    char      c;  // starts from any addresses divisible by 1(any addresses).
    char pad[3];  // not-used memory for `i` to start from its address.
    int32_t   i;  // starts from any addresses divisible by 4.

这类似于struct,但有一些区别。首先,我们可以说有两种填充——a)为了正确地从每个成员的地址开始,在成员之间插入一些字节。b)为了正确地从struct的地址启动下一个struct实例,将一些字节追加到每个struct:

// Example for rule 1 below.
struct st {
    char      c;  // starts from any addresses divisible by 4, not 1.
    char pad[3];  // not-used memory for `i` to start from its address.
    int32_t   i;  // starts from any addresses divisible by 4.
};

// Example for rule 2 below.
struct st {
    int32_t   i;  // starts from any addresses divisible by 4.
    char      c;  // starts from any addresses.
    char pad[3];  // not-used memory for next `st`(or anything that has same
                  // alignment requirement) to start from its own address.
};

The struct's first member always starts from any addresses divisible by struct's own alignment requirement which is determined by largest member's alignment requirement(here 4, alignment of int32_t). This is different with normal variables. The normal variables can start any addresses divisible by its alignment, but it is not the case for struct's first member. As you know, the address of a struct is the same as the address of its first member. There can be additional padded trailing bytes inside a struct, making next struct(or next element in an array of structs) starting from its own address. Think of struct st arr[2];. To make arr[1](arr[1]'s first member) starting from an address divisible by 4, we should append 3 bytes at the end of each struct.

这是我从《丢失的结构包装艺术》中学到的。

注意:可以通过_Alignof操作符来研究数据类型的对齐要求。同样,你也可以通过offsetof宏来获取结构中成员的偏移量。

填充和填充只是同一事物的两个方面:

包装或对齐是每个成员四舍五入的大小 填充是为匹配对齐而添加的额外空间

在mystruct_A中,假设默认对齐为4,则每个成员以4字节的倍数进行对齐。因为char的大小是1,所以a和c的填充是4 - 1 = 3个字节,而int b不需要填充,因为它已经是4个字节了。它对mystruct_B的工作方式相同。

只有当你告诉编译器显式地对结构进行打包时,才会进行结构打包。你看到的是填充。您的32位系统正在填充每个字段以字对齐。如果您告诉编译器打包结构,它们将分别为6和5字节。但是不要这样做。它不可移植,使编译器生成的代码更慢(有时甚至有bug)。

填充将结构成员对齐到“自然”地址边界——例如,int成员将有偏移量,在32位平台上是mod(4) == 0。默认情况下,填充是开启的。它在你的第一个结构中插入以下“间隙”:

struct mystruct_A {
    char a;
    char gap_0[3]; /* inserted by compiler: for alignment of b */
    int b;
    char c;
    char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

另一方面,打包可以防止编译器进行填充-这必须显式地请求-在GCC下,它是__attribute__((__packked__)),因此如下:

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

会在32位架构上产生大小为6的结构。

不过需要注意的是,在允许未对齐内存访问的体系结构(如x86和amd64)上,未对齐内存访问速度较慢,并且在严格对齐的体系结构(如SPARC)上是明确禁止的。

结构填充抑制结构填充,当对齐最重要时使用填充,当空间最重要时使用填充。

一些编译器提供#pragma来抑制填充或将其打包为n个字节。有些公司提供关键字来做到这一点。通常,用于修改结构填充的pragma将采用以下格式(取决于编译器):

#pragma pack(n)

例如,ARM提供了__packed关键字来抑制结构填充。查阅编译器手册了解更多相关信息。

所以一个填充结构是一个没有填充的结构。

一般采用填料结构

为了节省空间 对数据结构进行格式化,以便在网络上传输 协议(这当然不是一个好的实践,因为您需要这样做 处理字节序)