如何绕过零大小的数组?
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;
另见
- What is the correct way of interop'ing with C flexible array members from C++?
- Why do some structures end with an array of size 1?
- Are flexible array members valid in C++?
- bmiColors field of BITMAPINFO structure
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]);
在我工作的图书馆中,我发现了这样的结构:
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;
另见
- What is the correct way of interop'ing with C flexible array members from C++?
- Why do some structures end with an array of size 1?
- Are flexible array members valid in C++?
- bmiColors field of BITMAPINFO structure
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]);