#include<iostream>
using namespace std;
struct a{
int e;
struct abc *d;
};
struct abc{
int c;
};
int main()
{
return 0;
}
我在struct abc
的定义之后定义了struct a
,其中我已经为abc
声明了一个结构指针。这应该抛出编译错误,因为abc
在声明之前使用。但是,它没有,为什么?
而当我用struct abc d
而不是struct abc *d
替换它时,它会按预期给出编译错误。
答案 0 :(得分:6)
此声明
struct abc *d;
一方面声明struct abc
,另一方面声明d
类型的指针struct abc *
。
在此声明中,不需要具有struct abc的确切定义,因为不使用该结构的数据成员。
此说明符struct abc
称为详细类型说明符。
它在给定范围内引入了一个新类型或引用了一个已声明的类型。
答案 1 :(得分:5)
你是对的,通常,你需要这种类型的前瞻声明:
// Forward declaration
struct bar;
struct foo
{
bar* d; // only a pointer-to-bar; forward declaration sufficient
};
struct bar
{
int c;
};
但是,您(出于某种原因)使用在类型名称之前编写struct
的过时习语。 (这在C中是必需的,但从未在C ++中使用过。)
struct foo
{
struct bar* d;
};
struct bar
{
int c;
};
因为你在那里写struct bar
而不只是bar
,本身就算作各种各样的前瞻性声明。编译器现在知道bar
是一种类型,这就是它需要知道的全部内容。
这有点模糊和微妙,但这就是为什么你不再需要事先的前瞻声明了。
[C++11: 3.1/4]:
[注意: 类名也可以由 elaborated-type-specifier 隐式声明(7.1。 6.3)。 -end note]
[C++11: 3.3.2/6]:
首先在 elaborated-type-specifier 中声明的类的声明点如下:
表格
的声明class-key attribute-specifier-seq opt identifier
;
标识符在包含声明的范围内声明为 class-name ,否则
表格的详细说明类型
类密钥标识符
如果在定义的函数的 decl-specifier-seq 或 parameter-declaration-clause 中使用 elaborated-type-specifier 在命名空间作用域中,标识符在包含声明的命名空间中声明为类名;否则,除了作为朋友声明之外, 标识符在包含声明的最小名称空间或块范围中声明。 [注意:这些规则也适用于模板。 -end note] [注意:其他形式的 elaborated-type-specifier 不声明新名称, 因此必须引用现有的类型名称。见3.4.4和7.1.6.3。 -end note]
[C++11: 3.4.4/2]:
[..] 如果类密钥引入 elaborated-type-specifier 并且此查找未找到先前声明的 type-name ,或者 elaborated-type-specifier 出现在声明中,格式为:class-key attribute-specifier-seq opt identifier
;
elaborated-type-specifier 是一个声明,它引入了 class-name ,如3.3.2 中所述。
答案 2 :(得分:2)
它有效,因为编译器具有所需的所有信息:
d
(=指针大小) 1 的大小,abc
是什么(它是struct
)。如果您存储了struct abc
类型的对象:
struct abc d;
它会导致错误,因为缺少有关d
的大小和内存布局的信息(因为struct abc
尚未定义)。
此外,如果您遗漏了struct
关键字:
abc *d;
然后会丢失有关abc
的内容的信息,这也是一个错误。
这应该抛出编译错误,因为在声明之前使用了
abc
。
它不是使用,它只是声明。通过指针使用abc
首先需要解除引用指针(如果此时未定义abc
,则会出错)。
1 允许指向不完整类型的指针,因为指针的大小和内存布局并不取决于指向的内容。
答案 3 :(得分:0)
允许声明为不完整类型的指针。
这是前瞻性声明 -
前向声明是实际定义之前的声明,通常用于在定义不可用时引用声明的类型。当然,并非所有事情都可以使用声明未定义的结构来完成,但在某些情况下可以使用它。这种类型称为不完整。
以下表格的声明
class-key attr identifier ; // struct abc *d; (Your case)
声明一个类类型,稍后将在此范围内定义。在定义出现之前,此类名称的类型不完整。
当声明struct a
时,它还不知道struct abc
的规格,但您可以转发它。
答案 4 :(得分:-2)
在struct a
中,您将指针引用到struct abc
,因此编译器无需更多有关它的信息来计算d
的大小构件。
在稍后阶段,它将检查struct abc
是否已定义(如果需要)(例如,如果它被取消引用)。这解释了标准的基本原理
禁止声明未知类型的变量(abc
,未转发时,是一种未知类型)
允许将它们声明为指向不完整类型的指针。 (struct abc
是一种不完整的类型:至少知道它是struct
}。
实际上,将结构声明为
struct a {
struct abc *d;
}
等于转发(struct)类型abc
的声明,如
struct abc; // forward
struct a {
abc *d; // legal
};
但是abc
类型不完整,因此以下内容是非法的
struct z {
struct abc y; // error : incomplete struct type
}
为了好奇,这没关系:
struct A {
struct B* ptr1; // forwards declaration of B, and use it
B* ptr2; // B is a known (incomplete-)type name now
}