我们可以使用链接中指定的零长度数组初始化结构:
我正在使用以下结构:
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对象具有所需的内存,我们可以用任何我们想要的方式进行类型转换...
答案 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;