简体   繁体   English

为 32 位 alignment 填充 C 中的打包结构

[英]Pad a packed struct in C for 32-bit alignment

I have a struct defined that is used for messages sent across two different interfaces.我定义了一个结构,用于通过两个不同的接口发送消息。 One of them requires 32-bit alignment, but I need to minimize the space they take.其中之一需要 32 位 alignment,但我需要尽量减少它们占用的空间。 Essentially I'm trying to byte-pack the structs, ie #pragma pack(1) but ensure that the resulting struct is a multiple of 32-bits long.本质上,我正在尝试对结构进行字节打包,即#pragma pack(1) ,但要确保生成的结构是 32 位长的倍数。 I'm using a gcc arm cross-compiler for a 32-bit M3 processor.我正在为 32 位 M3 处理器使用 gcc arm 交叉编译器。 What I think I want to do is something like this:我想我想做的是这样的:

#pragma pack(1)
typedef struct my_type_t
{
    uint32_t someVal;
    uint8_t anotherVal;
    uint8_t reserved[<??>];
}
#pragma pack()

where <??> ensures that the size of my_type_t is divisible by 4 bytes, but without hard-coding the padding size.其中<??>确保my_type_t的大小可被 4 个字节整除,但不对填充大小进行硬编码。 I can do something like this:我可以做这样的事情:

#pragma pack(1)
typedef struct wrapper_t
{
    my_type_t m;
    uint8_t reserved[sizeof(my_type_t) + 4 - (sizeof(my_type_t) % 4)]
}
#pragma pack()

but I'd like to avoid that.但我想避免这种情况。

Ultimately what I need to do is copy this to a buffer that is 32-bit addressable, like:最终我需要做的是把它复制到一个 32 位可寻址的缓冲区中,比如:

static my_type_t t; //If it makes a difference, this will be declared statically in my C source file
...
memcpy(bufferPtr, (uint32_t*)&t, sizeof(t)) //or however I should do this

I've looked at the __attribute__((align(N))) attribute, which gives me the 32-bit aligned memory address for the struct, but it does not byte-pack it.我查看了__attribute__((align(N)))属性,它为我提供了结构的 32 位对齐 memory 地址,但它没有对它进行字节打包。 I am confused about how (or if) this can be combined with pack(1) .我对如何(或是否)可以将其与pack(1)结合使用感到困惑。

My question is this:我的问题是这样的:

What is the right way to declare these structs so that I can minimize their footprint in memory but that allows me to copy/set it in 4-byte increments with a unsigned 32-bit pointer?声明这些结构的正确方法是什么,以便我可以最大限度地减少它们在 memory 中的占用空间,但这允许我使用无符号 32 位指针以 4 字节增量复制/设置它? (There are a bunch of these types of arbitrary size and content). (有一堆这些类型的任意大小和内容)。 If my approach above of combining pack and padding is going about this totally wrong, I'll happily take alternatives.如果我上面结合pack和 padding 的方法完全错误,我会很乐意采取替代方案。

Edit:编辑:

Some constraints: I do not have control over one of the interfaces.一些限制:我无法控制其中一个界面。 It is expecting byte-packed frames.它期待字节打包的帧。 The other side is 32-bit addressable memory mapped registers.另一边是 32 位可寻址 memory 映射寄存器。 I have 64k of memory for the entire executable, and I'm limited on the libraries etc. I can bring in. There is already a great deal of space optimization I've had to do.对于整个可执行文件,我有 64k 的 memory,而且我的库等有限。我可以引入。我已经做了很多空间优化。

The struct in this question was just to explain my question.这个问题中的结构只是为了解释我的问题。 I have numerous messages of varying content that this applies to.我有许多不同内容的消息适用于此。

As you use gcc you need to use one of the attributes.当您使用 gcc 时,您需要使用其中一个属性。

Example + demo.示例 + 演示。

#define PACKED __attribute__((packed))
#define ALIGN(n) __attribute__((aligned(n)))

typedef struct 
{
    uint8_t anotherVal;
    uint32_t someVal;
}PACKED my_type_t;


my_type_t t = {1, 5};
ALIGN(64) my_type_t t1 = {1, 5};
ALIGN(512) my_type_t t2 = {2, 6};

int main()
{
    printf("%p, %p, %p", (void *)&t, (void *)&t1, (void *)&t2);
}

Result:结果:

0x404400, 0x404440, 0x404600

https://godbolt.org/z/j9YjqzEYW https://godbolt.org/z/j9YjqzEYW

I suggest combining #pragma pack with alignas :我建议将#pragma packalignas结合使用:

#include <stdalign.h>
#include <stdint.h>

typedef struct {
    #pragma pack(1)
    alignas(4) struct {      // requires 2+1+2 bytes but is aligned to even 4:s
        uint16_t someVal;    // +0
        uint8_t anotherVal;  // +2
        uint16_t foo;        // +3 (would be 4 without packing)
    };
    #pragma pack()
} my_type_t;

The anonymous inside struct makes access easy as before:内部的匿名struct使访问像以前一样容易:

int main() {
    my_type_t y;
    y.someVal = 10;
    y.anotherVal = 'a';
    y.foo = 20;

    printf("%zu\n", (char*)&y.someVal - (char*)&y.someVal);    // 0
    printf("%zu\n", (char*)&y.anotherVal - (char*)&y.someVal); // 2
    printf("%zu\n", (char*)&y.foo - (char*)&y.someVal);        // 3

    my_type_t x[2];
    printf("%zu\n", (char*)&x[1] - (char*)&x[0]); // 8 bytes diff
}

If you'd like to be able to take the sizeof the actual data carrying part of my_type_t (to send it), you could name the inner struct (which makes accessing the fields a little more cumbersome):如果您希望能够获取sizeof的实际数据携带部分的my_type_t (发送它),您可以命名内部struct (这使得访问字段更加麻烦):

#pragma pack(1)
typedef struct {
    uint16_t someVal;
    uint8_t anotherVal;
    uint16_t foo;
} inner;
#pragma pack()

typedef struct {
    alignas(4) inner i;
} my_type_t;

You'd now have to mention i to access the fields, but it has the benefit that you can take sizeof and get 5 (in this example):您现在必须提及i才能访问这些字段,但它的好处是您可以使用sizeof并获得5 (在此示例中):

int main() {
    my_type_t y;

    printf("%zu %zu\n", sizeof y, alignof(y));   // 8 4
    printf("%zu\n", sizeof y.i);                 // 5    (the actual data)
}

I can't speak for the specific compiler and architecture you are using, but I would expect the following to be sufficient:我不能代表您正在使用的特定编译器和体系结构,但我希望以下内容就足够了:

typedef struct {
   uint32_t x;
   uint8_t  y;
} my_type_t;

The structure normally has the same alignment as its largest field, and that includes adding the necessary padding at the end.该结构通常具有相同的 alignment 作为其最大字段,其中包括在末尾添加必要的填充。

my_type_t
+---------------+
| x             |
+---+-----------+
| y | [padding] |
+---+-----------+

|<-- 32 bits -->|

Demo演示

This is done so the fields are properly aligned when you have an array of them.这样做是为了在您拥有一组字段时正确对齐字段。

my_type_t my_array[2];

my_array[1].x = 123;  // Needs to be properly aligned.

The above assumes you have control over the order of the fields to get the best space efficiency, because it relies on the compiler aligning the individual fields.以上假设您可以控制字段的顺序以获得最佳空间效率,因为它依赖于编译器对齐各个字段。 But those assumptions can be removed using GCC attributes.但是可以使用 GCC 属性删除这些假设。

typedef struct {
   uint8_t  x;
   uint32_t y;
   uint8_t  z;
}
   __attribute__((packed))      // Remove interfield padding.
   __attribute__((aligned(4)))  // Set alignment and add tail padding.
   my_type_t;

This produces this:这会产生:

my_type_t
+---+-----------+
| x | y         
+---+---+-------+
    | z | [pad] |
+---+---+-------+

|<-- 32 bits -->|

Demo演示

The packed attribute prevents padding from being added between fields, but aligning the structure to a 32-bit boundary forces the alignment you desire. packed属性可防止在字段之间添加填充,但将结构对齐到 32 位边界会强制使用您想要的 alignment。 This has the side effect of adding trailing padding so you can safely have an array of these structures.这具有添加尾随填充的副作用,因此您可以安全地拥有这些结构的数组。

To form a structure type that is aligned one must put the alignment attribute to the first member of the struct.要形成对齐的结构类型,必须将 alignment 属性放在结构的第一个成员中。 It can be combined with the packed attribute.它可以与packed属性结合使用。

typedef struct {
    _Alignas(4) uint8_t anotherVal;
    uint32_t someVal;
} __attribute__((packed)) my_type_t;

Exemplary usage with alignment exaggerated to 64 bytes. alignment 的示例用法被夸大为 64 字节。

#include <stdint.h>
#include <stdio.h>

typedef struct {
    _Alignas(64) uint8_t anotherVal;
    uint32_t someVal;
} __attribute__((packed)) my_type_t;

int main() {
    my_type_t a, b;
    printf("%zu %p\n", sizeof a, (void*)&a);
    printf("%zu %p\n", sizeof b, (void*)&b);
}

prints:印刷:

64 0x7ffff26caf80
64 0x7ffff26cafc0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM