所有结构标识符都自动向前声明

All struct identifiers are automatically forward declared

在回答 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 identifier 视为前向声明的不完整 struct 类型?

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

struct identifier 是对象类型。

6.2.5 Types

  1. The meaning of a value stored in an object or returned by a function is determined by the type of the expression used to access it. (An identifier declared to be an object is the simplest such expression; the type is specified in the declaration of the identifier.) Types are partitioned into object types (types that describe objects) and function types (types that describe functions). At various points within a translation unit an object type may be incomplete (lacking sufficient information to determine the size of objects of that type) or complete (having sufficient information). 37)

37) A type may be incomplete or complete throughout an entire translation unit, or it may change states at different points within a translation unit.

  1. An array type of unknown size is an incomplete type. It is completed, for an identifier of that type, by specifying the size in a later declaration (with internal or external linkage). A structure or union type of unknown content (as described in 6.7.2.3) is an incomplete type. It is completed, for all declarations of that type, by declaring the same structure or union tag with its defining content later in the same scope.

6.7.2.3 Tags

  1. All declarations of structure, union, or enumerated types that have the same scope and use the same tag declare the same type. Irrespective of whether there is a tag or what other declarations of the type are in the same translation unit, the type is incomplete 129) until immediately after the closing brace of the list defining the content, and complete thereafter.

129) An incomplete type may only by used when the size of an object of that type is not needed. It is not needed, for example, when a typedef name is declared to be a specifier for a structure or union, or when a pointer to or a function returning a structure or union is being declared. (See incomplete types in 6.2.5.) The specification has to be complete before such a function is called or defined.

标准说 (6.2.5.28)

All pointers to structure types shall have the same representation and alignment requirements as each other.

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

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

Anystruct tag 的使用算作结构类型的(前向)声明,如果之前没有声明的话。虽然更正式的方式是说这只是一种类型,因为 C 标准没有提到 "forward declarations of structure types",只是完整和 incomplete structure types (6.2.5p22).

6.7.2 Type specifiers tells us that a struct-or-union-specifier is a type-specifier, and 6.7.2.1 Structure and union specifiers paragraph 1 告诉我们 struct identifier 是一个结构或联合说明符.

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

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

那么这个不完整类型的"implicit forward declaration"对于这个结构的工作是必不可少的。毕竟,在终止分号处输入 struct node is only complete。但是你需要引用它来声明 next 指针。

此外,struct node 声明(不完整类型)可以超出范围,就像任何其他声明一样。例如,如果您有一些原型

,就会发生这种情况
int function(struct unknown *parameter);

其中 struct unknown 在声明结束时立即超出范围。任何进一步声明的 struct unknown 都与这个不同。这在 6.2.5p22:

的文本中暗示

A structure or union type of unknown content (as described in 6.7.2.3) is an incomplete type. It is completed, for all declarations of that type, by declaring the same structure or union tag with its defining content later in the same scope.

这就是 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);

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

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 自引用结构”部分说:

Occasionally, one needs a variation of self-referential structures: two structures that refer to each other. The way to handle this is:

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

我同意,可以用另一种方式实现,但我认为这是 C 语言作者的良好动机,我同意他们的看法,这是实现它的优雅方式。