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

时间:2013-01-30 18:39:21

标签: c struct

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

Zero-Length

我正在使用以下结构:

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结构。即内存应该如下:

 -------------------------------------------------------------
| CommandHeader.len | CmdXHeader.len | CmdXHeader.payload ....|
 -------------------------------------------------------------

我可以轻松地将malloc 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对象具有所需的内存,我们可以用任何我们想要的方式进行类型转换...

4 个答案:

答案 0 :(得分:9)

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

这是“结构黑客”的几种形式之一。稍微便于实现的方法是定义长度为1而不是0的数组。

C语言的创造者丹尼斯·里奇(Dennis Ritchie)称其为“C实现的无根据性”。

1999年的ISO C标准修订版引入了一个名为“灵活阵列成员”的功能,这是一种更加强大的方法。大多数现代C编译器都支持这个功能(我怀疑微软的编译器不支持)。

comp.lang.c FAQ的问题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_t中定义的标准类型uint8_t<stdint.h>(假设您的编译器支持它;它在C99中也是新的)。

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

  
      
  • 所有以下划线和大写字母或其他字母开头的标识符   下划线总是保留用于任何用途。
  •   
  • 所有以下划线开头的标识符始终保留用作标识符   在普通名称和标签名称空间中都有文件范围。
  •   

还有几类保留标识符。

但是,不是确定哪些标识符在文件范围内可以安全使用,哪些标识符可以安全地在其他范围内使用,而是避免定义以下划线开头的任何标识符要容易得多。)

答案 1 :(得分:1)

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

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];

重要!除非你打包你的结构,它可能会在单词边界上对齐而不是可移植的。有关如何打包结构的特定语法,请参阅编译器文档。

另外,如果您消耗字节(即从文件或线路中读取),我只会执行您已经显示的内容。如果您是数据的创建者,那么我会衷心地建议您反对您所展示的内容。

答案 2 :(得分:0)

   struct _CommandHeader *commandHeader = malloc(sizeof(struct _CommandHeader)+
                                  sizeof(struct _CmdXHeader));

答案 3 :(得分:0)

最好在C99中使用payload []而不是payload [0]。 一些C99编译器不鼓励使用零长度数组。 所以,如果你在这里收到错误:

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

您可以随时将其更正为:

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