【问题标题】:DIfference in structs?结构的区别?
【发布时间】:2013-12-16 16:50:26
【问题描述】:

两者有什么区别:

typedef struct part 
{
   int a;
} Part;

typedef struct 
{
   int a;
} Part;

我知道第二个是“匿名的”,但它们有什么不同吗?

【问题讨论】:

  • 在第二种情况下,您将无法将类型称为struct part。当您在创建 struct part *child 必须是该结构的成员的自引用(列表或树)数据结构时,差异是显着的。
  • @SylvainDefresne 这也是有效的 C++ 代码,OP 并没有说他们只对 C 感兴趣。
  • 除了自引用的潜在用途之外,这两个结构之间没有运行时差异。如果这是您问题的主要要点。
  • 他们有不同的名字,和struct A { }struct B { }一样的区别

标签: c++ c data-structures


【解决方案1】:

除了 Mike Seymour 的回答之外,这里有一个示例说明原因:

例如链表

// this works
typedef struct part {
    struct part *next;
} Part;

// this doesn't work
typedef struct {
    struct Part *next;
} Part;
// neither this
typedef struct {
    Part *next;
} Part;

【讨论】:

  • 为什么?做这个限制有什么意义?为什么不允许你做第二件事?
  • 当 Part 尚未定义时,编译器如何知道它是什么(在第二个示例中)?
  • 但是当struct part *next的定义还没有完成的时候,编译器怎么知道它是什么?
  • @dfg 编译器不需要知道类的定义,只要它知道它就在那里。这就像向前声明一些东西。
  • @dfg 对于指针或引用,您不需要完整的类型定义。你只需要知道类型存在。
【解决方案2】:

在这两种情况下,您都定义了一个结构类型和一个名为 Part 的类型别名,它引用该类型。

在第一种情况下,您还定义了一个结构名称part,因此结构类型也可以称为struct part,或者在C++中只是part

在第二种情况下,结构本身没有名称(因此是“匿名的”),只能通过类型别名来引用。由于该别名仅在结构定义之后声明,因此该结构不能引用自身。

【讨论】:

  • 但只要你可以引用它,你又何必关心如何引用它呢?
  • @dfg:您只能在结构定义中(Part 声明之前)通过结构名称引用它 - 并且只有当它有一个时。
  • 为什么?做这个限制有什么意义?为什么不允许你自己引用它?
  • @dfg:一般来说,您只能在名称被声明后才能使用它们。该限制的重点是允许在单遍中解析程序,从而使编译速度比其他方式快得多。
【解决方案3】:

除了@EoiFirst 的回答,

typedef struct part 
{
   int a;
   struct part *next;
} Part;

这是有效的,因为next 是指向struct part指针,因此它具有指针类型的大小。

C 是一种类型化语言,所以编译器知道next 的类型,在struct part 完全定义之后,它就能确定下面的代码是有效的。它知道next 是一个指向struct part 的指针,它有一个字段int a

void test(struct part *p) {
    if (p != NULL && p->next != NULL) {
        printf("%d\n", p->next->a); // this is valid
    }
}

你可以声明

typedef struct part 
{
   int a;
   void *next;
} Part;

struct part 的大小仍然与上面声明的相同。现在next 是一个指向任何东西的指针,上面带有test() 的代码会编译出错。

warning: dereferencing 'void *' pointer
error: request for member 'a' in something not a structure or union

你需要写printf("%d\n", ((struct part *) p->next)->a);

此外,您将无法声明这一点,

typedef struct part 
{
   int a;
   struct part nested;
} Part;

你会得到一个编译器错误。

error: field 'nested' has incomplete type

【讨论】:

    【解决方案4】:

    @MikeSeymur 和其他人给出了很好的答案。我想补充一个没有人明确说明的重点。

    在大型项目中,重要的是尽量减少标题并避免#include-ing 其他标题,除非绝对必要。

    假设您的结构是在某个头文件part.h 中定义的。定义的命名结构类型Part 允许其他使用您的代码的人通过使用所谓的“前向声明”跳过#include "part.h"

    //file foo.h knows struct Part "by name only"
    struct Part;
    void foo(struct Part* p); //in pure C, works for C++ too
    void bar(Part* p); //in C++
    

    在匿名结构的情况下,文件foo.h被强制#include文件part.h

    //file foo.h forced to #include part.h because "part" is typedef of anonymous type
    #include "part.h"
    void foo(part* p); 
    

    在遗留代码中,这些匿名类型通常会导致每个标头都包含许多其他标头,以至于维护和代码重用成为一场噩梦。

    一般来说,我建议不要使用匿名类型,除非您确切地知道自己在做什么(在很多情况下它们完全没问题,例如作为其他类型的实现成员)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多