什么是“打包”? C 中的结构?
我正在查看一些为 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
定义结构时,允许编译器添加填充(没有实际数据的空间),以便成员落在 CPU 更容易访问的地址边界内。
例如,在 32 位 CPU 上,32 位成员应从 4 字节的倍数地址开始,以便有效访问(读取和写入)。以下结构体定义在两个成员之间添加了 16 位填充,以便第二个成员落在正确的地址边界内:
在 32 位体系结构中,上述结构体在内存中的结构体为 (~ = padding):
当结构体被打包时,这些填充不会被插入。编译器必须生成更多代码(运行速度较慢)来提取未对齐的数据成员,并写入它们。
打包后,相同的结构将在内存中显示为:
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:
The structure in memory of the above structure in a 32-bit architecture is (~ = padding):
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:
它指示编译器不要在
struct
成员之间添加任何填充。例如,请参阅此页面。
It instructs the compiler to not add any padding between members of the
struct
.See, for example, this page.
让我解释一下结构中填充的概念
然后通过举例来打包结构。
然后让我们看看为什么需要包装。
填充:
当在 16 位架构上按上述方式声明结构时,变量
abc
将被分配一些地址。下一个地址不会分配给变量xyz
,而是添加一个额外的字节,然后将下一个地址分配给变量xyz
。最后,结构如下所示:
填充使微控制器可以轻松访问成员变量的地址。
缺点是图像中出现了额外的不必要的字节。
打包:
如果使用属性“
packed
”声明相同的结构,则变量abc
后面不会添加额外的字节。让我举一个需要打包的例子:
考虑一个与 EEPROM 连接的微控制器,其中存储了一些结构。
想象一下写入 EEPROM 的函数如下所示:
现在,如果不进行打包,多余的填充字节将占用 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:
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 variablexyz
, instead one extra byte is added, and then the next address would be assigned to the variablexyz
.In the end, the structure looks something like below:
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 variableabc
.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:
Now if packing is not done, the extra padded bytes would occupy space in the EEPROM, which is of no use.
尚未明确指出的一件事是,打包通常是为了匹配预定义的字段结构。例如,在网络接口的低层,一系列字节在联网的机器之间交换。接收到数据后,需要将其映射到高层结构,以便可以轻松地操作数据。这时通常不需要填充,以便结构直接映射到字节。
网络数据交换还涉及字节字节序问题(即几乎所有网络数据都使用大字节序格式,而不管源机器和目标机器的字节序如何)。
此外,有些机器无法访问非对齐地址中的宽数据,例如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.
_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".当在结构声明期间使用打包时,编译器不会向同一结构的成员添加任何填充。下面是示例代码和输出,这是不言自明的。
编译
运行|输出
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.
Compile
Run|Output
因为除了减少内存占用之外,当将结构从最大成员手动打包到最小成员时,所有内存访问通常最终位于偶数对齐的内存地址,这在某些微处理器上会显着提高性能。
某些微处理器无法访问奇数内存地址处大于一个字节的内存,并且会引发陷阱,导致代码崩溃。
当在奇数地址访问大于一个字节的内存、需要更多代码、使缓存无效或两者兼而有之时,一些微处理器会遭受严重处罚。
请参阅 Eric S. Raymond 的精彩著作《结构包装的失落艺术》:
http://www. catb.org/esr/structural-packing/
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/