【问题标题】:All struct identifiers are automatically forward declared所有结构标识符都自动向前声明
【发布时间】:2015-08-23 21:37:10
【问题描述】:

在回答warning: assignment from incompatible pointer type for linklist array 时,我注意到任何带有struct 关键字的未声明标识符都被视为前向声明的标识符。

例如program below 编译良好:

/* Compile with "gcc -std=c99 -W -Wall -O2 -pedantic %" */
#include <stdio.h>

struct foo 
{
    struct bar *next;  /* Linked list */
};


int main(void) {
    struct bar *a = 0;
    struct baz *b = 0;
    struct foo c = {0};

    printf("bar -> %p\n", (void *)a);
    printf("baz -> %p\n", (void *)b);
    printf("foo -> %p, %zu\n", (void *)&c, sizeof c); /* Remove %zu if compiling with -ansi flag */
    return 0;
}

我的问题:哪条规则指导C 编译器将未声明的struct identifiers 视为前向声明的不完整struct 类型?

【问题讨论】:

  • 我不知道,但可能是因为在声明 struct anything 的过程中,您需要能够使用 struct anything,也许编译器不会处理所有这些,而只是处理它是现有的吗?

标签: c struct c99 incomplete-type ansi-c


【解决方案1】:

标准说 (6.2.5.28)

所有指向结构类型的指针都应具有相同的表示和对齐要求。

这意味着编译器知道如何表示指向任何结构的指针,即使是那些(尚未)未定义的结构。
您的程序只处理指向此类结构的指针,所以没关系。

【讨论】:

  • 这听起来合乎逻辑。这不是使前向声明无用吗。
  • 在使用指向结构的指针的特殊情况下,是的。在使用指向它的指针之前声明一个不完整的结构是不必要的。
  • 但是为不完整的类型创建typedef 可以用于允许编译器使用 typedef 对后面的参数列表进行类型检查,而不是简单地传递 void 指针。很好的答案。
  • 6.2.5.28 并不是很相关。它更像是“为什么它应该工作”的一种基本原理,但没有解决任何关于结构声明(完整或不完整)的问题。不过感谢(草案)标准的链接:它比我以前知道的要好。
【解决方案2】:

在 6.2.5 类型和 6.7.2.3 标签中有描述。

struct identifier 是一个对象类型。

6.2.5 类型

  1. 存储在对象中或由函数返回的值的含义由 用于访问它的表达式的类型。 (声明为对象的标识符是 最简单的这种表达方式;类型在标识符的声明中指定。)类型 分为对象类型(描述对象的类型)和函数类型(类型 描述功能)。 在翻译单元内的不同点,对象类型可能是 不完整(缺乏足够的信息来确定该类型对象的大小)或 完整(有足够的信息)。 37)

37) 一个类型在整个翻译单元中可能是不完整或完整的,或者它可能会在 翻译单元内的不同点。

  1. 未知大小的数组类型是不完整的类型。它已完成,对于标识符 该类型,通过在以后的声明中指定大小(具有内部或外部链接)。 未知内容的结构或联合类型(如 6.7.2.3 中所述)是不完整类型。对于该类型的所有声明,通过声明相同的结构或 联合标记及其定义内容稍后在同一范围内。

6.7.2.3 标签

  1. 结构、联合或枚举类型的所有声明具有相同的范围和 使用相同的标签声明相同的类型。无论是否有标签或什么 该类型的其他声明在同一个翻译单元中,类型不完整 129) 直到紧接在定义内容的列表的右大括号之后,并完成 之后。

129) 不完整类型只能在不需要该类型对象的大小时使用。它不是 需要,例如,当 typedef 名称被声明为结构或联合的说明符时,或 当一个指针或一个返回结构或联合的函数被声明时。(见不完整类型 在 6.2.5 中。)在调用或定义此类函数之前,必须完成规范。

【讨论】:

    【解决方案3】:

    除了 2501 提供的答案,以及您对它的评论“在我的情况下,甚至没有前向声明”之外,还有以下内容。

    任何使用 struct tag 都算作结构类型的(前向)声明,如果之前没有声明过的话。虽然更正式的方式是说这只是一种类型,但由于 C 标准没有提到“结构类型的前向声明”,只是完整的和incomplete structure types (6.2.5p22)

    6.7.2 Type specifiers 告诉我们 struct-or-union-specifier 是一个 type-specifier,而6.7.2.1 Structure and union specifiers paragraph 1 又告诉我们 struct identifier 是一个 struct-or-union-specifier

    假设你有一个链表声明,类似于

    struct node {
        struct node *next;
        int element;
    };
    

    那么这个不完整类型的“隐式前向声明”对于这个结构的工作是必不可少的。毕竟,类型 struct node is only complete 在终止分号处。但是您需要引用它才能声明next 指针。

    此外,struct node 声明(类型不完整)可能超出范围,就像任何其他声明一样。例如,如果您有一些原型,就会发生这种情况

    int function(struct unknown *parameter);
    

    struct unknown 在声明结束时立即超出范围。任何进一步声明的struct unknowns 都与这个不同。 6.2.5p22的文字中暗示了这一点:

    未知内容的结构或联合类型(如 6.7.2.3 中所述) 是不完全类型。它已完成,对于所有声明 类型,通过声明与其定义相同的结构或联合标记 稍后的内容在同一范围内

    这就是 gcc 发出警告的原因:

    foo.c:1:21: warning: 'struct unknown' declared inside parameter list
    foo.c:1:21: warning: its scope is only this definition or declaration, which is probably not what you want
    

    您可以通过在其前面添加一个额外的前向声明来解决此问题,这会使范围更早开始(因此更晚结束):

    struct unknown;
    int function(struct unknown *parameter);
    

    【讨论】:

      【解决方案4】:

      我认为使用不完整结构类型的最优雅的用例是这样的:

      struct foo 
      {
          struct bar *left;
          struct bar *right;
      };
      struct bar
      {
          int something;
          struct foo *next;
      };
      

      即双重递归,其中a指向b,b指向a。 这种情况可能是这个特性被包含在原始 C 语言规范中的一个原因。

      最初的问题是所有结构标识符是否都自动向前声明。我认为最好说所有不完整的结构定义都被自动视为前向声明

      编辑:在关于文档的评论之后,让我们看看 C 语言圣经:Kerninghan&Ritchie - The C Programming Language,“6.5 自引用结构”部分说:

      有时,需要一种自引用结构的变体: 两个相互引用的结构。处理方法是:

      struct t {
          ...
          struct s *p;   /* p points to an s */
      };
      struct s {
          ...
          struct t *q;   /* q points to a t */
      };
      

      我同意,有可能以另一种方式实现,但我认为这是 C 语言作者的良好动机,我同意他们的观点,即这是一种优雅的实现方式。

      【讨论】:

      • 感谢您的回答。在struct foo上方前向声明struct bar可以很好地实现功能,所以这不能算真正的动机。除了自动转发声明的动机之外,我还在寻找它在规范中的记录。
      • 谢谢。在我的版本中我可以在 P115 上看到这一点,虽然作者选择不详细说明这一点,但是是的,看起来内容是经过深思熟虑的,没有跳过任何细节。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-09
      • 1970-01-01
      • 2018-11-06
      • 1970-01-01
      • 1970-01-01
      • 2021-10-19
      • 1970-01-01
      相关资源
      最近更新 更多