4

在嵌入式世界中,我们经常拥有通过固定长度缓冲区传递的数据结构。使用这样的东西相对容易处理:

#define TOTAL_BUFFER_LENGTH 4096
struct overlay {
    uint16_t field1;
    uint16_t field2;
    uint8_t  array1[ARY1_LEN];
};
static_assert(sizeof(struct overlay) <= TOTAL_BUFFER_LENGTH);

struct overlay* overlay = malloc(TOTAL_BUFFER_LENGTH);

也就是说,我们使用数据结构作为覆盖层,以允许轻松访问当前正在使用的缓冲区部分。

然而,我们有许多缓冲区格式,它们也使用缓冲区的最后几个字节来存储校验和等内容。我们目前使用这样的结构:

struct overlay {
    uint16_t field1;
    uint16_t field2;
    uint8_t  array1[ARY1_LEN];
    char     reserved[TOTAL_BUFFER_LENGTH -
                      sizeof(uint16_t) - sizeof(uint16_t) -
                      (sizeof(uint8_t) * ARY1_LEN) -
                      sizeof(uint32_t)];
    uint32_t crc;
};

这个简单的数据结构看起来很丑陋,但当结构增长到有几十个字段时,这绝对是一个怪物。这也是可维护性的噩梦,因为添加或删除结构字段意味着reserved必须同时更新大小计算。

当结构的末尾只包含一项(如校验和)时,我们有时会使用辅助函数来读取/写入值。这使数据结构保持干净和可维护,但是当缓冲区的末尾有多个字段时,它不能很好地扩展。

如果我们可以做这样的事情会很有帮助:

struct overlay {
    uint16_t field1;
    uint16_t field2;
    uint8_t  array1[ARY1_LEN];
    char     reserved[TOTAL_BUFFER_LENGTH -
                      offsetof(struct overlay, reserved) -
                      sizeof(uint32_t)];
    uint32_t crc;
};

不幸的是,offsetof仅适用于完整的对象类型,并且由于它位于 的定义的中间,因此struct overlay该类型还不完整。

有没有一种更清洁、更易于维护的方式来做这种事情?我基本上需要一个固定长度的结构,在开头和结尾都有字段,中间的剩余空间保留/未使用。

4

3 回答 3

4

在您的情况下,我想我可能会这样做:

typedef struct overlay_head
{
    uint16_t field1;
    uint16_t field2;
    uint8_t  array1[ARY1_LEN];
} overlay_head;

typedef struct overlay_tail
{
    uint32_t crc;
} overlay_tail;

enum { OVERLAY_RSVD = TOTAL_BUFFER_LENGTH - sizeof(overlay_head)
                                          - sizeof(overlay_tail) };

typedef struct overlay
{
    overlay_head h;
    uint8_t      reserved[OVERLAY_RSVD];
    overlay_tail t;
} overlay;

然后,您几乎可以像以前一样工作,除了以前写的地方p->field1 现在写p->h.field1,以前写的地方p->crc现在写p->t.crc

请注意,这可以非常有效地处理任意大的尾部结构,只要头部和尾部都适合整体大小。

于 2012-07-21T01:29:34.020 回答
1

您可以定义一个结构,该结构仅在末尾包含带有 CRC 字段的缓冲区:

struct checked_buffer {
  char data[TOTAL_BUFFER_LENGTH - sizeof(uint32_t)];
  uint32_t crc;
};

然后将你的“覆盖”放在它的data领域。您可能已经将指针“转换”为原始缓冲区,因此当您想要访问 CRC 字段时,将其转换char*为tooverlay*应该不是什么大问题。overlay*checked_buffer*

但是如果你想让一个字段在一堆结构中处于一致的位置,把它放在每个结构的开头会更容易。这样,您可以直接在每个结构中声明它,而无需做任何奇怪的事情,并且您不需要任何指针转换来访问它。

于 2012-07-21T00:26:15.537 回答
0

那个怎么样?

union a256
{
    struct
    {
        int field_a;
        int field_b;
        char name[16];
        // 
        int crcshadow;
    };

    struct
    {
        char buff[256-sizeof(int)];
        int crc;
    };
} ;

static_assert(offsetof(a256, crcshadow) < offsetof(a256, crc), "data too big");

第一个结构包含数据,第二个定义此联合的固定大小。

于 2014-10-01T10:05:29.267 回答