为什么这个C ++代码没有编译错误?

时间:2015-08-07 10:38:07

标签: c++ pointers struct structure

#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替换它时,它会按预期给出编译错误。

5 个答案:

答案 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
}