什么是“打包”? C 中的结构?

发布于 2024-10-28 19:21:41 字数 350 浏览 2 评论 0原文

我正在查看一些为 Microchip C30 编译器编写的 C 代码,经常看到如下定义的结构:

typedef struct __attribute__((__packed__)) 
{
    IP_ADDR     MyIPAddr;               // IP address
    IP_ADDR     MyMask;                 // Subnet mask
    IP_ADDR     MyGateway;              // Default Gateway
        // etc...
} APP_CONFIG;

《packed》是什么意思?

I am going though some C code written for the Microchip C30 compiler and I often see structs defined as follows:

typedef struct __attribute__((__packed__)) 
{
    IP_ADDR     MyIPAddr;               // IP address
    IP_ADDR     MyMask;                 // Subnet mask
    IP_ADDR     MyGateway;              // Default Gateway
        // etc...
} APP_CONFIG;

What does packed mean?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

终遇你 2024-11-04 19:21:41

定义结构时,允许编译器添加填充(没有实际数据的空间),以便成员落在 CPU 更容易访问的地址边界内。

例如,在 32 位 CPU 上,32 位成员应从 4 字节的倍数地址开始,以便有效访问(读取和写入)。以下结构体定义在两个成员之间添加了 16 位填充,以便第二个成员落在正确的地址边界内:

struct S {
    int16_t member1;
    int32_t member2;
};

在 32 位体系结构中,上述结构体在内存中的结构体为 (~ = padding):

+---------+---------+
| m1 |~~~~|   m2    |
+---------+---------+

当结构体被打包时,这些填充不会被插入。编译器必须生成更多代码(运行速度较慢)来提取未对齐的数据成员,并写入它们。

打包后,相同的结构将在内存中显示为:

+---------+---------+
| m1 |   m2    |~~~~
+---------+---------+

When structures are defined, the compiler is allowed to add paddings (spaces without actual data) so that members fall in address boundaries that are easier to access for the CPU.

For example, on a 32-bit CPU, 32-bit members should start at addresses that are multiple of 4 bytes in order to be efficiently accessed (read and written). The following structure definition adds a 16-bit padding between both members, so that the second member falls in a proper address boundary:

struct S {
    int16_t member1;
    int32_t member2;
};

The structure in memory of the above structure in a 32-bit architecture is (~ = padding):

+---------+---------+
| m1 |~~~~|   m2    |
+---------+---------+

When a structure is packed, these paddings are not inserted. The compiler has to generate more code (which runs slower) to extract the non-aligned data members, and also to write to them.

The same structure, when packed, will appear in memory as something like:

+---------+---------+
| m1 |   m2    |~~~~
+---------+---------+
只是偏爱你 2024-11-04 19:21:41

它指示编译器不要在 struct 成员之间添加任何填充。

例如,请参阅此页面

It instructs the compiler to not add any padding between members of the struct.

See, for example, this page.

农村范ル 2024-11-04 19:21:41

让我解释一下结构中填充的概念
然后通过举例来打包结构。

然后让我们看看为什么需要包装。

填充:

struct eg_struct
{
           unsigned char abc;
           unsigned int  xyz;
}

当在 16 位架构上按上述方式声明结构时,变量 abc 将被分配一些地址。下一个地址不会分配给变量xyz,而是添加一个额外的字节,然后将下一个地址分配给变量xyz

最后,结构如下所示:

struct eg_struct
{
           unsigned char abc;
           unsigned char paddedbytes[1];
           unsigned int  xyz;
}

填充使微控制器可以轻松访问成员变量的地址。
缺点是图像中出现了额外的不必要的字节。

打包:

如果使用属性“packed”声明相同的结构,则变量abc后面不会添加额外的字节。

让我举一个需要打包的例子:

考虑一个与 EEPROM 连接的微控制器,其中存储了一些结构。

想象一下写入 EEPROM 的函数如下所示:

Write_EEPROM(EEPROM address, Ram address, Byte count);

现在,如果不进行打包,多余的填充字节将占用 EEPROM 中的空间,这是没有用的。

Let me explain the concept of padding in structures and
then packed structures by taking an example.

And then let us see why packing is required.

Padding:

struct eg_struct
{
           unsigned char abc;
           unsigned int  xyz;
}

When the structure is declared as above on a 16 bit architecture, the variable abc would be assigned some address. The next address is not assigned to variable xyz, instead one extra byte is added, and then the next address would be assigned to the variable xyz.

In the end, the structure looks something like below:

struct eg_struct
{
           unsigned char abc;
           unsigned char paddedbytes[1];
           unsigned int  xyz;
}

Padding makes addresses of member variables easily accessible to the microcontroller.
The disadvantage is extra unnecessary bytes that come into the picture.

Packing:

If same structure is declared using the attribute “packed”, the extra byte will not be added after the variable abc.

Let me give one example where packing is needed:

Consider a microcontroller interfaced with an EEPROM where some structure is being stored.

Imagine a function writing to the EEPROM would look as below:

Write_EEPROM(EEPROM address, Ram address, Byte count);

Now if packing is not done, the extra padded bytes would occupy space in the EEPROM, which is of no use.

烟雨扶苏 2024-11-04 19:21:41

尚未明确指出的一件事是,打包通常是为了匹配预定义的字段结构。例如,在网络接口的低层,一系列字节在联网的机器之间交换。接收到数据后,需要将其映射到高层结构,以便可以轻松地操作数据。这时通常不需要填充,以便结构直接映射到字节。

网络数据交换还涉及字节字节序问题(即几乎所有网络数据都使用大字节序格式,而不管源机器和目标机器的字节序如何)。

此外,有些机器无法访问非对齐地址中的宽数据,例如Cortex-M0内核无法访问非32位对齐地址中的32位数据,因此写入时必须小心在这种情况下的网络代码。

One thing that has not been explicitly called out is that packing usually is done to match predefined field structures. For example, at the low level layer of a network interface, a series of bytes is exchanged between networked machines. After the data is received, it will need to be mapped to a high level structure so that the data can be manipulated easily. This is when no-padding is usually necessary, so that the structure directly maps to the bytes.

Network data interchange also involves byte endianness issue (i.e. almost all network data uses big endian format regardless the endianness of the source and destination machines).

Furthermore, some machines cannot access wide data in non-aligned address, for example, Cortex-M0 cores cannot access 32-bit data in non-32-bit aligned address, so care must be taken on writing networking code in such cases.

往日 2024-11-04 19:21:41

_attribute__((__packed__)) 意味着(最有可能)“不插入任何填充以使事情更快”,也可能意味着“不插入任何对齐以保持对齐”。

_attribute__((__packed__)) means (most probably) "do not insert any padding to make things faster" and may also mean "do not insert any alignments to preserve alignment".

对你再特殊 2024-11-04 19:21:41

当在结构声明期间使用打包时,编译器不会向同一结构的成员添加任何填充。下面是示例代码和输出,这是不言自明的。

$ cat structure_packed.c
#include <stdio.h>

typedef struct __attribute__((__packed__))
{
        char a;
        int ai;
        char ac;
}A;

struct B
{
        char b;
        int bi;
        char bc;
};

int main()
{
         A a;
        struct B b;
        int c;
        printf("size of struct A: %lu, addr a: %p, addr ai: %p, addr ac: %p\n", sizeof(a), &(a.a), &(a.ai), &a.ac);
        printf("size of struct B: %lu, addr b: %p, addr bi: %p, addr bc: %p\n", sizeof(b), &(b.b), &(b.bi), &b.bc);
        printf("addr of c: %p\n", &c);
        return 0;
}

编译

$ gcc structure_packed.c -o structure_packed

运行|输出

<前><代码>$ ./struct_packed
结构A的大小:6,地址a:0x7ffc6f177ed6,地址ai:0x7ffc6f177ed7,地址ac:0x7ffc6f177edb
结构 B 的大小:12,地址 b:0x7ffc6f177edc,地址 bi:0x7ffc6f177ee0,地址 bc:0x7ffc6f177ee4
c的地址:0x7ffc6f177ed0

When packed used during structure declaration, compiler will not add any padding to the members of the same structure. Below is the example code and output which is self explanatory.

$ cat structure_packed.c
#include <stdio.h>

typedef struct __attribute__((__packed__))
{
        char a;
        int ai;
        char ac;
}A;

struct B
{
        char b;
        int bi;
        char bc;
};

int main()
{
         A a;
        struct B b;
        int c;
        printf("size of struct A: %lu, addr a: %p, addr ai: %p, addr ac: %p\n", sizeof(a), &(a.a), &(a.ai), &a.ac);
        printf("size of struct B: %lu, addr b: %p, addr bi: %p, addr bc: %p\n", sizeof(b), &(b.b), &(b.bi), &b.bc);
        printf("addr of c: %p\n", &c);
        return 0;
}

Compile

$ gcc structure_packed.c -o structure_packed

Run|Output

$ ./structure_packed
size of struct A: 6, addr a: 0x7ffc6f177ed6, addr ai: 0x7ffc6f177ed7, addr ac: 0x7ffc6f177edb
size of struct B: 12, addr b: 0x7ffc6f177edc, addr bi: 0x7ffc6f177ee0, addr bc: 0x7ffc6f177ee4
addr of c: 0x7ffc6f177ed0
云朵有点甜 2024-11-04 19:21:41

如果打包结构会让事情变慢,为什么要创建它呢?

因为除了减少内存占用之外,当将结构从最大成员手动打包到最小成员时,所有内存访问通常最终位于偶数对齐的内存地址,这在某些微处理器上会显着提高性能。

某些微处理器无法访问奇数内存地址处大于一个字节的内存,并且会引发陷阱,导致代码崩溃。

当在奇数地址访问大于一个字节的内存、需要更多代码、使缓存无效或两者兼而有之时,一些微处理器会遭受严重处罚。

请参阅 Eric S. Raymond 的精彩著作《结构包装的失落艺术》:

http://www. catb.org/esr/structural-packing/

why would one create a packed structure if it makes things slower?

Because in addition to reducing the memory footprint, when the structure is manually packed from its largest to its smallest members, all memory access usually ends up being at an even-aligned memory address, which on some microprocessors significantly increases performance.

Some microprocessors cannot access memory larger than a byte at an odd memory address and will incur a trap, causing the code to crash.

Some microprocessors suffer severe penalties when accessing memory larger than a byte at an odd address, needing more code, invalidating the cach(es), or both.

See Eric S. Raymond's excellent "The Lost Art of Structure Packing":

http://www.catb.org/esr/structure-packing/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文