【问题标题】:Unsized array declaration in a struct结构中未调整大小的数组声明
【发布时间】:2013-11-26 15:19:03
【问题描述】:

为什么 C 允许这样做:

typedef 结构体 { int arr[]; } s;

数组arr 没有指定大小?

【问题讨论】:

  • 标准 C 不允许显示的结构。结构中必须至少有一个命名成员以及灵活数组成员或 FAM(并且 FAM 必须在最后一个)。最小的结构可能是typedef struct s { int size; int arr[]; } s;,它允许您记录数组的大小(在size 成员中)。这个大小几乎肯定是能够可靠地访问数组成员所必需的。
  • 尝试使用不允许灵活数组的 (UCSD) Pascal 之类的语言,您会喜欢此功能。

标签: c arrays flexible-array-member


【解决方案1】:

这是称为灵活数组的 C99 功能,主要功能是允许 use variable length array like features inside a structR.. 在此 answer to another question on flexible array members 提供一系列好处在指针上使用灵活数组draft C99 standard 部分 6.7.2.1 结构和联合说明符16 段中的 draft C99 standard 说:

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能 数组类型不完整;这称为灵活数组成员。在大多数情况下, 灵活的数组成员被忽略。特别是,结构的大小就像 省略了灵活的数组成员,除了它可能有更多的尾随填充 遗漏将意味着。 [...]

因此,如果您有一个s*,除了 struct 所需的空间之外,您还需要为数组分配空间,通常结构中还有其他成员:

s *s1 = malloc( sizeof(struct s) + n*sizeof(int) ) ;

标准草案实际上在第17段中有一个指导性示例:

EXAMPLE 声明后:

  struct s { int n; double d[]; };

结构结构s 有一个灵活的数组成员d。一个典型的使用方法 是:

   int m = /* some value */;
   struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

假设调用malloc成功,则p指向的对象 在大多数情况下,其行为就像 p 被声明为:

    struct { int n; double d[m]; } *p;

(在某些情况下,这种等价性会被打破;特别是, 成员d 的偏移量可能不一样)。

【讨论】:

  • 它使用 gcc -std=c89 编译
  • @sashan gcc 可能允许它作为 C99 之外的扩展,就像它对 VLA 等其他功能一样,如果您使用 -pedantic,它应该在这种情况下提供警告。刚试过gcc警告警告:ISO C90不支持灵活的数组成员[-pedantic]
  • @sashan 看起来很奇怪,编译器选项-std= 不强制执行标准合规性。为了根据给定的标准进行编译,您必须使用gcc -std=c89 -pedantic-errors。这将正确给出“错误:ISO C90 不支持灵活的数组成员”
  • OP 的例子实际上是不正确的。该结构必须至少有两个成员,其中一个可以是灵活数组成员。
  • @2501 是的,我猜这是对灵活数组成员的 GNU 扩展,嗯。
【解决方案2】:

您可能正在寻找 C99 中的灵活数组。 灵活的数组成员是结构/联合末尾大小未知的成员。

作为一种特殊情况,结构的最后一个元素具有多个 命名成员可能具有不完整的数组类型;这被称为 灵活的数组成员。在大多数情况下,灵活的数组成员 被忽略。特别是,结构的大小就像 灵活的数组成员被省略了,除了它可能有更多 尾随填充比省略所暗示的要多。

你也可以先看看reason for the struct hack

尚不清楚它是否合法或可移植,但它相当受欢迎。该技术的实现可能如下所示:

    #include <stdlib.h>
    #include <string.h>

    struct name *makename(char *newname)
    {
        struct name *ret =
            malloc(sizeof(struct name)-1 + strlen(newname)+1);
                    /* -1 for initial [1]; +1 for \0 */
        if(ret != NULL) {
            ret->namelen = strlen(newname);
            strcpy(ret->namestr, newname);
        }

        return ret;
    }

这个函数分配一个名字结构的实例 调整大小,以便 namestr 字段可以保存请求的名称 (不只是一个字符,正如结构声明所暗示的那样)。

尽管它很受欢迎,但该技术也有些臭名昭著- Dennis Ritchie 称其为“与 C 实现毫无根据的亲密关系。” 官方解释认为它不是 严格符合 C 标准,尽管它似乎确实有效 在所有已知的实现下。检查数组边界的编译器 小心可能会发出警告。

【讨论】:

  • 我很好奇 Dennis Ritchie 会提倡什么而不是 struct hack?恕我直言,最好的替代方案是让编译器不会在零大小数组声明时发出尖叫声,在这种情况下,编译器可以安全地假设,如果结构中的数组声明的大小为N,则不会尝试访问N-1u 之外的任何元素或最后一个占用空间的元素,以较低者为准。
  • OP 的例子实际上是不正确的。该结构必须至少有两个成员,其中一个可以是灵活数组成员。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-01
  • 1970-01-01
  • 2015-06-22
  • 2012-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多