C-包装结构的结合



我想创建一个不同尺寸的不同结构的数组。

所得的数组必须紧密包装,结构之间没有零值。

整个过程必须在编译时初始化,因此它可以驻留在嵌入式系统的闪光灯中。

结果是USB配置描述符的树,最后一个在最后一次产生单个配置斑点后立即包装的描述符。对问题的不同方法的建议将受到欢迎。http://www.beyondlogic.org/usbnutshell/usb5.shtml#configurationdescriptors

struct a {
    uint16_t some_field;
};
struct b {
    uint32_t another_field;
};
union detail {
    struct a a;
    struct b b;
};
const union detail configuration[] = {
    { .a = { .some_field = 23 } },
    { .b = { .another_field = 12 } }
};

上面的示例是我当前(失败,尝试)的显着简化版本。阵列的每个元素都是最大的工会成员的大小。因此,每个数组成员都是32位,第一个条目是用零填充的。

当前输出1700 0000 0c00 0000

所需的输出1700 0c00 0000

现有的生成此包装输出的方法使用带有宏的巨型UINT8数组来插入更复杂的值,例如16位数字。结构的数组更准确地表示数据并提供类型安全性,如果可以使用。

我不需要能够从数组中索引或访问数据,将斑点推入低级USB例程。使用GCC包装的属性没有改变标准的联合数组行为。

我想创建一个具有不同尺寸的不同结构的数组。

在C中根本不可能(出于充分的理由)。阵列(在C中)由相同大小(和类型)的组件组成。如果不是这种情况,那么对该数组元素的索引访问将是一个非常复杂且耗时的操作(这是符合C的精神;但是在C 中,您可以定义您自己的operator [])。

您可以使用char -S的数组(例如const char data[] = {0x35, 0x27, 0};等;也许可以通过某些Ad -Hoc脚本发出一些C代码来初始化一个大数组),并具有一些解析程序来处理它。或者您可以有一个 pointers 的数组:

union detail {
  struct a* aptr;
  struct b* bptr;
};
static const struct a firstelem= {.some_field= 35};
static const struct b secondelem= {.another_field= 12};
const union detail configuration[] = {
  {.aptr= &firstelem},
  {.bptr= &secondelem},
};

请注意,在您的情况下,有一系列指针实际上给出了更大的数据。

您不应该使用 union为此't做您认为的事情。如果您想要一系列结构,每个结构可能是不同类型的,则无法完成。相反,您必须按正确顺序定义一个包含所有其他结构的"超级结构"。

但是,这不能解决对齐/填充的问题。为了在结构(和工会)中禁用填充物,您必须诉诸于非标准C。一个常见的非标准扩展名是#pragma pack。GCC编译器还支持非标准属性"包装",请参阅" 属性>(((包装,对齐(4)))的含义是什么"。由于禁用填充的代码是非标准的,因此也不可容纳。

也可以通过创建uint8_t的数组来解决问题,然后在此数组中读取/写入数据块。这被称为数据的序列化/去序列化。从任何指针类型转换为uint8_t*或角色类型都是安全的,但不幸的是,围绕调用不确定的行为而采取的另一种方式。这是因为C语言中的错误通常称为"严格的混叠规则",这有时使得在执行与硬件相关的编程时,不可能以平滑或有意义的方式使用C语言。

此C语言错误的工作范围是用2个元素编写一个巨大的联合,一个是uint8_t数组,一个是一个"超级结构",如上所述。您实际上不会使用超级结构 - 您可能无法因为填充而无法使用 - 但是,通过将其放入联盟中,您可以提出一个特殊的例外,以严格的混叠。这意味着将不再有不确定的行为,您将防止像GCC这样的积极优化的编译器破坏您的代码。

此C语言错误的另一个特定于GCC特定的工作是使用gcc -fno-strict-aliasing编译。对于这种情况,嵌入式系统编译器通常比GCC更好,因为它们不遵循C标准,而是以非标准的方式进行指针转换行为决定性。例如,在这样的编译器代码(例如(uint16_t*)my_uint8t)上,确定性地将尖锐的数据视为uint16_t,而不是默默地导致程序崩溃并燃烧。

接受 @basile-starynkevitch, @jonathan-leffler和其他人的评论,我希望做到的一切都无法完成。我真正需要的是精确控制结构在内存/闪存中的相对位置。该位置是使用链接器完成的,我最终在那里找到了一个解决方案。

首先,在链接脚本的部分内部,我创建了一个特殊的块。确保订单的唯一方法是创建多个部分并在这种情况下手动订购CPACK0-3。

.text : ALIGN(4) /* Align the start of the block */
{
    *(.cpack0) *(.cpack1) *(.cpack2) *(.cpack3)
} > MFlash32

然后将结构变量插入特殊部分。长期的语法重复可以通过真实实现中的#Define元素来简化。

const struct a configuration __attribute__((section(".cpack0"), aligned(1))) = {
    .some_field = 23
};
const struct b configuration1 __attribute__((section(".cpack1"), aligned(1))) = {
    .another_field = 12
};

因此,我们有一个配置变量,该变量在4个字节地址对齐,以供尼斯访问,并使用结构为类型安全定义。配置的后续部分也由结构为安全性定义,并顺序放置在内存中。aligned(1)属性可确保它们紧紧地包装而没有空白空间。

这解决了我的问题,配置定义是通过提供所有优点的结构来完成的,丑陋的丑闻是由#define隐藏的,最终配置是由uint8_t*指针访问的可变长度的二进制斑点。随着指针的增加,它会在不同的配置元素上无缝移动。

最新更新