【问题标题】:zero length arrays [duplicate]零长度数组[重复]
【发布时间】:2011-01-14 11:49:24
【问题描述】:

最近遇到了一个结构定义,

struct arr {
    int cnt;
    struct {
        int  size;
        int *name;
    } list[0];
};

现在我不知道list[0] 被声明的原因。我感兴趣的是为什么要使用它。它有什么优势吗?如果是,那是什么?

【问题讨论】:

  • 零大小的对象在 C 中是非法的。要么使用 [1] 并浪费一点点空间(或计算以弥补它​​),要么使用 [] (但你的代码需要C99 编译器)。
  • 这是 gcc 手册中关于长度为零的数组的部分:gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

标签: c structure


【解决方案1】:

用于动态长度数组。您可以使用malloc() 分配内存,并将数组驻留在结构的末尾:

struct arr *my_arr = malloc(sizeof *my_arr + 17 * sizeof *my_arr->list);
my_arr->cnt = 17;
my_arr->list[0].size = 0;
my_arr->list[1].name = "foo";

实际上能够使用 0 作为长度是(如评论中所指出的)GCC 扩展。在 C99 中,您可以完全省略大小文字以达到相同的效果。

在实现这些东西之前,您经常会看到长度为 1 的情况,但这会使分配变得有点复杂,因为您必须在计算所需内存时进行补偿。

【讨论】:

  • 补充一点,作为一个长度为 0 的数组,list 不会影响结构的大小。 sizeof(struct arr) == 4
  • 使用长度 0 的能力是 GCC 功能。 C99 等效项是完全省略长度,如 struct { ... } list[];
  • 结构列表 { int size;诠释*名称; };结构 arr { int cnt;结构列表 *list_ptr; };这是等价的吗?
  • @hue: 否 - 这会将您的列表数组存储并分配到内存中的其他位置。这具有紧跟结构的列表。
  • @hue:呃……你刚刚得到了两个描述这种优势的答案。它允许您为结构和可变长度的数组分配内存作为一个连续内存块,只需调用malloc。如果您使用指针,则必须单独分配内存(两个 malloc 调用,可能不连续)或使用其他技巧(以实现正确对齐等)
【解决方案2】:

它被称为“结构破解”。您可以在 SO 或网络上搜索它

http://www.google.com/search?q=struct+hack&sitesearch=stackoverflow.com/questions

请注意,在 C 中正式声明大小为 0 的数组始终是非法的。您正式提供的代码甚至不可编译。大多数 C 编译器将接受 0 大小的数组声明作为扩展,特别是因为它经常用于“struct hack”的“惰性”版本(它可以依赖 sizeof 来确定要分配多少内存,因为 0-大小数组应该不会影响结构的总大小)。

一个可以说是更好的 struct hack 实现使用大小为 1 的数组

struct arr {
    int cnt;
    struct {
        int  size;
        int *name;
    } list[1];
};

它“更好”,因为它至少是正式可编译的。为了在list 中为具有N 元素的结构分配内存,使用标准的offsetof

arr *a = malloc(offsetof(arr, list) + N * sizeof a->list);

在 C99 版本的语言规范中,“struct hack”通过无大小数组声明(带有空 [])来支持,因为在 C99 中 0 大小的数组声明也是非法的。

【讨论】:

  • 标准中是否有任何内容禁止看到声明 unsigned char foo[1]; 的编译器编译对 foo[x] 的所有访问,就好像它们是 foo[0] 一样?虽然回答“struct-hack”问题的人认为 size-1-array 版本是否是“未定义行为”的问题是学术问题,但上述优化可能是有用的,特别是在某些嵌入式系统环境中,如果它是合法的。会不会(添加警告,编译器可能希望为间接访问的结构的最后一个元素显式禁用它)?
  • @supercat 如果我理解你的问题,它会默默地导致一些奇怪的行为,例如 &(foo[999]) == &(foo[0]) 。假设 a[b] === (&a[0])+b
  • @StevenR.Loomis:仅当b 小于a 中的元素数量时,您所做的假设才成立。如果结构中的数组产生的指针只能在数组的声明的大小或分配的空间的smaller内索引,那么您描述的场景将是未定义的行为。如果我负责标准,我会指定访问其指定范围之外的数组将是未定义行为 except 在特定情况下,结构末尾的单元素数组可能使用如果该结构是非零索引...
  • ...malloccalloc 等收到的一段内存中的最后一项。这样的规则将要求编译器继续使用“struct hack”支持旧代码,但将允许 struct array 强制允许的大多数优化(只是我提到的单元素情况,还有许多其他涉及别名的场景等。例如,给定 struct {int a[1], b;} foo; foo.b=3; x=foo.a[something()]; foo.b=0; 是否应该要求编译器存储 @ 987654337@在访问foo.a[]之前进入内存,还是应该允许省略存储?
  • @supercat 但运行时不知道“a”中有多少元素,我的假设只是重申我如何理解 C 来解释数组下标。我实际上会说场景是定义的行为(我现在正在使用它)。现在 b[999] 或 b[-100] 的 value 可能是未定义的,但我会持有的 behavior 是已定义的。我不希望编译器在这里检测范围错误。
【解决方案3】:

另一个优点是,如果您的结构描述了磁盘/网络数据。如果cnt为0,则数据大小可能只有cnt的长度。

我在这里只是为了确认我害怕的事情,list[0] 无效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 2014-07-25
    • 1970-01-01
    • 2021-04-06
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多