为什么这段代码在没有我的结构的前向声明的情况下编译?

Why does this code compile without forward declaration of my struct?

struct Figlio
{
    char nome[256];
    struct Genitore* padre;
    struct Genitore* madre;
};

struct Genitore
{
    char nome[256];
    struct Figlio* progenie;
};

int main()
{
    return 0;
}

我预计上面的代码不会编译,因为 struct Genitore 没有在 Figlio 之前声明,所以 Genitore 的前向声明(即 struct Genitore;顶部)是需要的。相反,在 C 中,它编译没有任何问题。

相反,如果我将一个尚未声明的结构指针传递给一个函数,例如,

void f(struct st*);

struct st{};

它引发以下警告

Warning: ‘struct st’ declared inside parameter list will not be visible outside of this definition or declaration

所以,我不明白为什么这些不同的行为,尤其是为什么不要求 struct Genitore;struct st; 的前向声明。

这是一个范围问题。

首先,关于 struct 标签的规则,C standard 的第 6.2.1p7 节指出:

Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. ...

这就是允许 struct 标记的出现在任何地方都充当声明的原因。这也允许结构包含指向自身的指针并使其引用相同的类型。

那么标识符范围的规则在6.2.1p4节中列出:

Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit. If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block. If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator. If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope

请特别注意两个粗​​体段落。当将指向该类型的指针声明为 struct Figlio 的成员时,struct Genitore 的隐式声明具有文件范围,因为它没有出现在块语句中,也没有出现在函数声明中,因此是从源文件中的那个点开始可见。

相比之下,函数声明中出现的隐式声明仅在声明本身的范围内。这样的类型声明是有问题的,因为您传入的任何潜在结构指针都将引用与函数声明中隐式声明的类型不同的类型。

在此声明中

struct Figlio
{
    char nome[256];
    struct Genitore* padre;
    struct Genitore* madre;
};

您声明了两个指向不完整类型的指针 struct Genitore

指针类型是一个完整的对象类型。

在这些声明中,您声明了不完整类型 struct Genitore 和指针类型的对象 struct Genitore *

结构类型struct Genitore的声明在声明结构Figlio的范围内可见(在文件范围或块范围内)

引入不完整结构类型的另一个示例是在 typedef 声明中声明它,例如

typedef struct A A;.

您可以在同一范围内的 typedef 声明之后的某个地方定义结构。

另一个例子是在函数声明的return类型中声明结构不完整类型。例如

struct A f( void );

同样,您至少需要在函数定义之前或函数调用之前定义结构。

这是一个演示程序。

#include <stdio.h>

struct A f( void );

struct A { int x; } f( void )
{
    struct A a = { .x = 10 };

    return a;
};    


int main( void )
{
    struct A a = f();

    printf( "a.x = %d\n", a.x );
}

程序输出为

a.x = 10

另一方面,如果您尝试取消引用指向不完整类型的指针,编译器将发出错误。

在此声明中

void f(struct st*);

类型说明符 struct st 具有函数原型范围并且在函数参数列表之外不可见。

来自 C 标准(6.2.1 标识符的范围)

  1. ...If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator.