3

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

零长度

我正在使用以下结构:

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 ....|
 -------------------------------------------------------------

我可以轻松地将 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

4 回答 4

9

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

它是“struct hack”的几种形式之一。一种更便携的方法是定义一个长度为 1 而不是 0 的数组。

C 语言的创建者 Dennis Ritchie 称其为“与 C 实现毫无根据的亲密关系”。

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

这在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_tuint8_t,定义在<stdint.h>(假设你的编译器支持它;它在 C99 中也是新的)。

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

  • 以下划线和大写字母或另一个下划线开头的所有标识符始终保留用于任何用途。
  • 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。

还有更多类别的保留标识符。

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

于 2013-01-30T18:52:48.423 回答
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];

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

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

于 2013-01-31T03:16:53.203 回答
0
   struct _CommandHeader *commandHeader = malloc(sizeof(struct _CommandHeader)+
                                  sizeof(struct _CmdXHeader));
于 2013-01-30T19:09:51.747 回答
0

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

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

您可以随时将其更正为:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[];
} CommandHeader;
于 2014-05-02T04:12:00.677 回答