我们可以使用链接中指定的零长度数组初始化结构:
零长度.
我使用以下结构:
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;
Run Code Online (Sandbox Code Playgroud)
现在,CommandHeader.payload应该指向/包含到CmdXHeader结构中.即内存应如下所示:
-------------------------------------------------------------
| CommandHeader.len | CmdXHeader.len | CmdXHeader.payload ....|
-------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
我可以轻松地将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);
Run Code Online (Sandbox Code Playgroud)
现在commandHeader对象具有所需的内存,我们可以用任何我们想要的方式进行类型转换...
在结构的末尾或其他任何地方的零长度数组实际上是非法的(更准确地说是约束违反)标准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;
/* ... */
Run Code Online (Sandbox Code Playgroud)
显然这只是一个粗略的轮廓.
请注意sizeof,当应用于该类型的类型CommandHeader或对象时,将为您提供不包含灵活数组成员的结果.
另请注意,以下划线开头的标识符保留给实现.您永远不应该在自己的代码中定义这样的标识符.不需要为typedef名称和struct标签使用不同的标识符:
typedef struct CommandHeader
{
UINT16 len;
UINT8 payload[0];
} CommandHeader;
Run Code Online (Sandbox Code Playgroud)
我还建议使用标准类型uint16_t和uint8_t定义<stdint.h>(假设您的编译器支持它;它也是C99中的新功能).
(实际上,以下划线开头的标识符规则稍微复杂一些.引用N1570,标准的最新草案,第7.1.3节:
- 所有以下划线开头的标识符以及大写字母或另一个下划线始终保留用于任何用途.
- 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符.
还有几类保留标识符.
但是,不是确定哪些标识符在文件范围内可以安全使用,哪些标识符可以安全地在其他范围内使用,而是避免定义以下划线开头的任何标识符要容易得多.)