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

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

我可以轻松地将 CmdXHeader/CommandHeader malloc 到自定义长度。但是如何为 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 的扩展。

它是“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 名称和结构标记使用不同的标识符:

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

我还建议使用标准类型 uint16_tuint8_t , 在 <stdint.h> 中定义(假设您的编译器支持它;它在 C99 中也是新的)。

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

  • All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
  • All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

还有几类保留标识符。

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

关于c - 如何在 C 中使用零长度数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14611322/

相关文章:

c - 如何修复此结构/链表脚本中的不兼容类型?

c - 如何使用非阻塞方式检查stdin是否为空

c - 从函数返回链表的头

C# : How does this work : Unit myUnit = 5;

go - 在 Go 中附加到结构 slice

c - 删除具有重复数据的项目

c - 当不与核心 Hook 时,应用程序运行速度更快

c - C 中循环控制台菜单实现的问题

c - 错误: Syntax error before 'struct'

arrays - 在 Swift 中将实例变量的属性保存到结构的静态变量数组