C/C++结构不完整的成员类型不一致?

Ala*_*ins 12 c c++ struct pointers

假设我们在C或C++中定义了两个结构(我在C和C++中得到相同的结果,我认为规则是相同的,告诉我它们是否不相同).

一个值不完整的值成员:

struct abc{
  int data;
  struct nonexsitnow next; //error: field ‘next’ has incomplete type, makes sense since "struct nonexsitnow" hasn't been declared or defined.
};
Run Code Online (Sandbox Code Playgroud)

以及一个不完整类型的指针成员:

struct abc{
  int data;
  struct nonexsitnow *next; //pass?!
};
Run Code Online (Sandbox Code Playgroud)

为什么第二个定义不会导致任何问题?它使用struct nonexsitnow尚未创建!

更新:

我在下面的答案和评论中总结了这张表,希望它们对于详细说明是正确和有用的.

正如@ArneVogel提到,无论是struct Foo p;struct Foo *p;是一个隐含的声明Foo,并struct Foo;明确地做这件工作(感谢@约翰布林).这对几乎没有任何意义,但对和行为有所不同:

               In a situation where struct Foo is:
_______________________________________________________
|               | undeclared | declared but | defined |
|               |            | not defined  |         |
-------------------------------------------------------
|               |  C | C++   |  C  |  C++   | C | C++ |
-------------------------------------------------------
|struct Foo  p; |  × |  ×    |  ×  |   ×    | ? |  ?  |
|struct Foo* p; |  ? |  ?    |  ?  |   ?    | ? |  ?  |
|       Foo  p; |  × |  ×    |  ×  |   ×    | × |  ?  |
|       Foo* p; |  × |  ×    |  ×  |   ?    | × |  ?  |
Run Code Online (Sandbox Code Playgroud)

眠りネ*_*ネロク 35

为什么第二个结构定义不会导致任何问题?

因为它是一个指针.即使指针指向的类型不完整,编译器也知道指针的大小.


Mat*_*jek 8

编译器需要struct/class的大小,以便知道在实例化这种类型时必须分配多少内存.

在给定平台上,sizeof(T*)对于作为结构或类类型的任何类型T ,将始终返回相同的值.

这就是为什么你可以使用指向前向声明类型的指针而没有错误.当然,为了访问由这样的指针指向的对象的内容或者取消引用它,必须提供定义.但是,您可以为此类指针赋值(只要在类型兼容性方面允许).

一个重要的事实:

在C中,您通常使用所谓的"C风格转换",通常可以执行指针分配,无论类型如何(您有责任确保正确的行为并满足对齐要求).

但是,在C++中,是否可以在不完整类型之间进行转换取决于转换的类型.考虑两种多态类型:

class A; // forward declaration only
class B; // forward declaration only, actually inherits from A

A* aptr;
B* bptr;

bptr = (B*)(aptr); // ok
bptr = dynamic_cast<B*>(aptr); // error
Run Code Online (Sandbox Code Playgroud)

dynamic_cast<>如果编译器无法访问转换中涉及的类型定义(这是执行运行时检查所必需的),则将失败.示例:Ideone.

  • 每个人似乎都错过了一个重要的观点:只需编写`struct Foo*f;`将隐含地向前声明`Foo`.这仅在使用`struct`等时才起作用,是来自C的继承行为,并且似乎是对OP的混淆点. (3认同)
  • 你说"*在给定的平台上,`sizeof(T*)`将始终返回相同的值,无论T是什么.*"这显然是错误的.如果你把自己局限于指向类类型的指针,你就可以了. (3认同)
  • 你错了.指向不同类型的指针,尤其是函数指针,可能会有所不同.虽然所有结构/类指针必须具有相同的表示. (2认同)

Joh*_*ger 6

首先,您需要了解类型"不完整"的含义.C以这种方式定义它:

在翻译单元内的各个点处,对象类型可能是不完整的(缺少足够的信息来确定该类型的对象的大小)或完整的(具有足够的信息).

(C2011,6.2.5 / 1)

请注意,类型完整性是声明的范围和可见性的函数,而不是类型的固有特征.类型可以在翻译单元中的一个点处不完整,并且在不同点处完成.

然而,

指针类型可以从函数类型或对象类型派生,称为引用类型.[...] 指针类型是完整的对象类型.

(C2011,6.2.5 / 20 ;重点补充)

因此,没有限定条件,所有指针类型都是完整类型,甚至是引用类型本身不完整的指针. 标准没有解决特定实现如何使这项工作,但通常,所有指针到结构类型具有相同的大小和表示(与其引用类型的表示无关).

这结果很重要,因为结构类型在其定义的右大括号之前是不完整的,所以如果指向不完整类型的指针本身不完整,那么结构不能包含指向其自身类型的另一个结构的指针,例如通常用于实现链表,树和其他数据结构.

另一方面,

未知内容的结构或联合类型[...]是不完整类型.对于该类型的所有声明,通过在稍后的同一范围内声明相同的结构或联合标记及其定义内容,它已完成.

(C2011,6.2.5 / 22)

这是有道理的,因为如果结构类型不知道它的成员是什么,编译器就无法知道它有多大.然后它更有意义

结构或联合不应包含具有不完整或函数类型的成员(因此,结构不应包含其自身的实例,但可以包含指向其自身实例的指针),除了结构的最后一个成员具有多个一个命名成员可能具有不完整的数组类型[...].

(C2011,6.7.2.1.1/3 ;重点补充)

该异常描述了一个名为"灵活数组成员"的C功能,它带有一些警告和限制.这是一个你可以单独阅读(或询问)的切线问题.

此外,所有上述内容都与C和C++允许您在声明其成员之前通过其标记引用结构类型这一事实一致; 也就是说,当它不完整时.这可以作为前瞻性声明自行完成......

struct foo;
Run Code Online (Sandbox Code Playgroud)

......但是这不是用于任何文件目的,因为不需要前面声明结构类型.您可以再次考虑链接列表的用法,但这种特性绝不仅限于此类上下文.

实际上,一个相对常见的用例是实现不透明类型.在这种情况下,由于各种原因,库生成并使用其不希望公开的实现的数据类型.然而,它可以将适当类型的指针分发给客户端代码,并期望接收这样的指针.如果它从不提供引用类型的定义,那么客户端代码必须将引用的对象视为不透明的blob.