如何绕过零大小的数组?

How to get around zero-sized arrays?

在我工作的图书馆中,我发现了这样的结构:

typedef struct {
    uint16_t id;
    uint16_t len;
    union {
        uint8_t data[0];
        uint16_t data16[0];
        uint32_t data32[0];
    }
} packet_t;

所以这是一个具有灵活长度数据的数据包,可以访问每个字长。

但是标准规定对象的大小不能为零,因此编译它会引发警告(“ISO C 禁止零大小数组”)。

我在 gcc 中编译代码,所以这个零大小的数组 hack 会因为它的扩展而做得很好。但是有什么办法可以让我真正“迂腐”吗?由于联合,灵活数组成员没有帮助。

如果我把多字节数据成员扔掉会很简单,但我想尽可能地保留它们,因为库中的一些函数依赖于它们。

起初我以为我可以用一个大小的数组来代替,但我不确定是否完全没有副作用。有什么建议吗?

在C99之前,没有符合标准的方式。在 C99 中,

typedef struct {
    uint16_t id;
    uint16_t len;
    uint8_t data[];
} packet_t;

是实现灵活长度结构的合法方式。

你的 union 几乎是无用的合成糖,无论如何:你需要从外部知道类型,而且 C 没有类型限制,所以无论你写 packet.data16[2] 还是 uint16_t* data = packet.data; data[2] 几乎没有区别。

没有符合标准的方法来将具有不同成员长度的 union 类型的数组作为最后一个元素。

生成使用 -pedantic 标志编译的东西并非不可能。但我认为如果没有 C11,您将无法在语法方面获得完全相同的语义。

变形后的packet_t可能看起来像这样

typedef union packet_t {
    struct { uint16_t id;   uint16_t len; };
    struct { uint16_t id8;  uint16_t len8;  uint8_t data[];    };
    struct { uint16_t id16; uint16_t len16; uint16_t data16[]; };
    struct { uint16_t id32; uint16_t len32; uint32_t data32[]; };
} packet_t;

匿名结构 (C11) 需要将字段嵌套为 packet_t 的成员。这就是 p->id 仍然有效的原因。其他 id*len* 字段是虚拟的,因为灵活的数组成员可能不是结构的唯一成员。根据联合的共同初始序列保证,它们在那里使访问格式正确。

看着它,我不禁觉得你最好完全放弃多个 data 字段。 uint8_t 灵活的数组成员可以包含其他两个可能包含的任何内容。

如果您使用 C99 之前的编译器,那么一个简单的常用技巧是将大小设置为 1。当然您还需要在处理它时 add/subtract 将大小设置为 1

typedef struct {
    uint16_t id;
    uint16_t len;
    union {
        uint8_t data[1];
        uint16_t data16[1];
        uint32_t data32[1];
    }
} packet_t;

另见

So this is a packet with flexible-lengthed data, with an access to each of the word lengths.

您不能在 C 中定义可变大小的数组(通常,在特殊情况下可以,但不常见)。该语言没有任何方法可以动态更改数组的大小,因为数组的长度与其声明相关联(必须在编译时知道)没有类型参数允许您指定当您声明这种类型的变量时,数组在结构中的元素数量。

变通方法附带指针。可以声明一个数组元素类型指针,并为其动态分配内存,如:

typedef struct {
    uint16_t id;
    uint16_t len;
    union {
        uint8_t *data;
        uint16_t *data16;
        uint32_t *data32;
    } payload;  /* you need to name the union field!!! */
} packet_t;

并通过调用 malloc() 初始化指针,提供适当的内存量。稍后,如果您改变主意,您可以 free() 指针并分配另一个指针(再次使用 malloc())和不同的大小。

    packet_t pkt;
    pkt.id = 23;
    pkt.len = 113;
    pkt.payload.data = malloc[pkt.len];
    ....
    /* access */
    printf("third 16bit data in the payload is: 0x%04x\n",
            pkt.payload.data16[2]);