如何在C语言中使用零长度数组

如何在C语言中使用零长度数组,c,struct,C,Struct,我们可以使用链接中指定的零长度数组初始化结构: 我使用以下结构: typedef unsigned char UINT8; typedef unsigned short UINT16; typedef struct _CommandHeader { UINT16 len; UINT8 payload[0]; } CommandHeader; typedef struct _CmdXHeader { UINT8 len; UINT8 payload[0]; }

我们可以使用链接中指定的零长度数组初始化结构:

我使用以下结构:

typedef unsigned char UINT8;
typedef unsigned short UINT16;

typedef struct _CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

typedef struct _CmdXHeader
{
    UINT8 len;
    UINT8 payload[0];
} CmdXhHeader;
现在CommandHeader.payload应该指向/包含CmdXHeader结构。i、 e.内存应该如下所示:

 -------------------------------------------------------------
| CommandHeader.len | CmdXHeader.len | CmdXHeader.payload ....|
 -------------------------------------------------------------
我可以轻松地将CmdXHeader/CommandHeader修改为自定义长度。但是如何为CmdXHeader有效负载赋值,或者如何将CmdXHeader对象链接到CommandHeader.payload


我的解决方案 谢谢你的回复。我用以下方法解决了这个问题:

//Get the buffer for CmdXHeader:
size_t cmdXHeader_len = sizeof(CmdXHeader) + custom_len;
CmdXHeader* cmdXHeader = (CmdXHeader*) malloc(cmdXHeader_len);
//Get a temporary pointer and assign the data to it
UINT8* p;
p[0] = 1;
p[2] = 2;
.......

//Now copy the memory of p to cmdXHeader
memcopy(cmdHeader->payload, p, custom_len);

// allocate the buffer for CommandHeader
CommandHeader* commandHeader = (CommandHeader*) malloc (sizeof (CommandHeader) + cmdXHeader_len);

// populate the fields in commandHeader
commandHeader->len = custom_len;
memcpy(commandHeader->payload, cmdXHeader, cmdXHeader_len);

现在commandHeader对象有了所需的内存,我们可以用我们想要的任何方式进行类型转换…

在结构末尾或其他任何地方的零长度数组在标准C中实际上是非法的(更准确地说是违反约束的)。它是特定于gcc的扩展

它是“结构黑客”的几种形式之一。一种更方便的方法是定义长度为1而不是0的数组

C语言的创建者丹尼斯·里奇(Dennis Ritchie)称之为“C语言实现的毫无根据的友好”

1999年的ISO C标准修订版引入了一种称为“柔性阵列成员”的功能,这是一种更稳健的方法。大多数现代C编译器都支持此功能(不过我怀疑微软的编译器不支持)

本报告问题2.6对此进行了详细讨论

至于如何访问它,无论使用哪种形式,都可以像对待任何数组一样对待它。在大多数上下文中,成员的名称会衰减为指针,从而允许您对其进行索引。只要分配了足够的内存,就可以执行以下操作:

CommandHeader *ch;
ch = malloc(computed_size);
if (ch == NULL) { /* allocation failed, bail out */ }
ch.len = 42;
ch.payload[0] = 10;
ch.payload[1] = 20;
/* ... */
显然,这只是一个粗略的轮廓

请注意,
sizeof
应用于类型
CommandHeader
或该类型的对象时,将得到不包括灵活数组成员的结果

还请注意,以下划线开头的标识符保留给实现。您不应该在自己的代码中定义这样的标识符。typedef名称和struct标记不需要使用不同的标识符:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;
我还建议使用
中定义的标准类型
uint16\u t
uint8\u t
(假设您的编译器支持它;它在C99中也是新的)

(实际上,以下划线开头的标识符规则稍微复杂一些。引用标准最新草案,第7.1.3节:

  • 以下划线和大写字母或其他字母开头的所有标识符 下划线始终保留供任何使用
  • 所有以下划线开头的标识符始终保留用作标识符 文件范围在普通和标记名空间中
另外还有几类保留标识符


但是,与其确定哪些标识符可以在文件范围内安全使用,哪些标识符可以在其他范围内安全使用,还不如避免定义任何以下划线开头的标识符。)

我假设您的内存中有一些字节,您想找到指向有效负载的指针吗

typedef struct _CmdXHeader
{
    UINT8 len;
    UINT8* payload;
} CmdXhHeader;

typedef struct _CommandHeader
{
    UINT16 len;
    CmdXhHeader xhead;
} CommandHeader;
然后,您可以将内存强制转换为指向CommandHeader的指针

uint8_t* my_binary_data = { /* assume you've got some data */ };

CommandHeader* cmdheader = (CommandHeader*) my_binary_data;

// access the data
cmdheader->xhead.payload[0];
重要!除非打包结构,否则它可能会在单词边界上对齐,并且不可移植。有关如何打包结构的特定语法,请参阅编译器文档


另外,如果您正在使用字节(即从文件或导线读取),我只会执行您所显示的操作。如果您是数据的创建者,那么我衷心建议您不要使用您所展示的内容。

最好在C99中使用payload[]而不是payload[0]。 有些C99编译器不鼓励使用零长度数组。 因此,如果您在此处遇到错误:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;
您始终可以将其更正为:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[];
} CommandHeader;

不在C.6.7.5.2.1中“…如果表达式是常量表达式,则其值应大于零”为什么不让
CommandHeader
具有
CmdXHeader
类型的成员?实际上CommandHeader是一个公共结构。它的有效负载可以有CmdXHeader CmdYHeader等…可能是+1的重复答案。真的很好奇。我认为前导下划线的标准位置是任何以两个前导下划线或一个前导下划线后跟大写字母开头的标识符都是为实现保留的。至少这是从C++到7.4.4.3.2的方法。并且很好地指出,OP违反了第二个条件。但是C是否会进一步限制所有以下划线开头的全局ID,包括带有一个下划线和小写字母的ID?@WhozCraig:我在回答中添加了一个更全面的解释。“请注意,当sizeof应用于CommandHeader类型或该类型的对象时,将给出一个不包含灵活数组成员的结果。”我认为单凭这个理由就足以不这样做。我已经重新格式化了你的代码,这样它就可以在不需要侧滚的情况下读取。但是强制转换是不必要的,可能会掩盖错误。只要您有所需的
#include
,就可以将
malloc()的结果直接分配给指针;
void*
结果将隐式转换为适当的类型。感谢Sibrajas的评论。但是,我尝试使用典型的零长度数组本身。我知道如果我把它改成一个指针,事情会非常简单,并且可以按照你指定的那样使用。
typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[];
} CommandHeader;