【问题标题】:Is it possible to consistently refer to forward-declared and non-forward-declared structs in C?是否可以在 C 中一致地引用前向声明和非前向声明的结构?
【发布时间】:2017-01-08 10:49:27
【问题描述】:

这不适用于foo

struct Foo;

typedef struct
{
    int x;
}
Bar;

void foo (Foo *); // unknown type name ‘Foo’

void bar (Bar *);

typedef struct
{
    int y;
}
Foo;

这不适用于bar

struct Foo;

typedef struct
{
    int x;
}
Bar;

void foo (struct Foo *);

void bar (struct Bar *); ‘struct Bar’ declared inside parameter list

typedef struct
{
    int y;
}
Foo;

我的一些结构必须是前向声明的,因为它们是作为指针传递的,而其中一些必须不是前向声明的,因为它们是作为值传递的。

有没有一种方法可以在 C 中声明类型,以便所有函数原型始终以相同的方式引用自定义类型,无论它们是否被前向声明?

【问题讨论】:

  • 在你的例子中没有struct Foo。有一个没有标签的结构,你 typedef 为Foo
  • C 不是 C++....
  • 为了减少噪音并以此减少混乱,只需删除所有typedefs。 typedefs 根本不需要来让事情顺利进行。
  • 类型定义不是前向声明结构的定义。这是完全不同的事情。
  • 在任何地方使用struct Foostruct Bar,如void bar (struct Bar *),可以消除这个问题。

标签: c struct typedef forward-declaration function-prototypes


【解决方案1】:

您的问题不是前向声明与非前向声明,而是struct Xtypedef struct { ... } X

您可以仅使用struct X 解决此问题:

struct Foo;

struct Bar
{
    int x;
};

void foo (struct Foo *);
void bar (struct Bar *);

struct Foo
{
    int y;
};

一旦你有了,你可以介绍typedef名字:

typedef struct Foo Foo;

typedef struct
{
    int x;
}
Bar;

void foo (Foo *);
void bar (Bar *);

struct Foo
{
    int y;
};

你不能预先声明 typedefs,所以我们仍然需要一个真正的 struct 类型,我们可以转发到。这里有一个小不一致:Foostruct Foo 相同,但是没有struct Bar,只有Bar。您可以通过切换到来解决此问题

typedef struct Bar
{
    int x;
}
Bar;

这同时定义了struct BarBar

【讨论】:

    【解决方案2】:
    struct Foo;
    

    声明一个名为Foo 的结构。但是,您必须声明一个名为 Foo 的结构的 typedef Foo

    #include  <stdio.h>
    
    typedef struct Foo Foo;
    
    typedef struct {
        int x;
    } Bar;
    
    void foo(Foo *);
    
    void bar(Bar *);
    
    struct Foo {
        int y;
    };
    
    int main(void) {
        printf("Hello, world!\n");
    }
    

    【讨论】:

      【解决方案3】:

      首要任务是指出在您的示例中没有struct Foo。只有一个没有标签的结构,你 typedefFoo1

      有没有一种方法可以在 C 中声明类型,以便所有函数原型始终以相同的方式引用自定义类型,无论它们是否被前向声明?

      struct 的定义或声明应出现在函数参数列表中使用结构之前。否则声明范围只是函数原型,这几乎肯定不是你想要的。

      只有通过严格的编码才能实现您想要的目标。

      前向声明是函数原型真正需要的一切。在定义或调用函数本身之前,您不需要完整的结构定义。

      struct Foo;
      void foo (struct Foo); // Okay, only at the call site or definition site
                             // do we need the full struct definition.
      

      一个简短的example 演示

      #include <stdio.h>
      
      struct Foo;
      void foo (struct Foo);
      
      struct Foo
      {
          int y;
      };
      
      int main(void) {
      
          struct Foo f = { .y = 1 };
          foo(f);
      
          return 0;
      }
      
      void foo (struct Foo f)
      {
          printf("%d", f.y);
      }
      

      定义和声明分布在不同的翻译单元中时,这更清楚了,但以上必须这样做。

      所以我给你的建议是,在将它们用于函数原型(通过指针或值)之前,你总是在单独的行上声明你的结构。在真正需要之前不要引入完整的定义。


      1fa.linux.kernel 上的一封可爱的信件,Linus Torvalds 比我清楚地表达了为什么你应该更喜欢使用完整的 struct 标签。

      【讨论】:

      • 在您提供的link 上讨论了关于使用typedefs 的精彩讨论。谢谢你。
      【解决方案4】:

      C 支持的唯一“前向声明”和引用是指针,例如:

      void myFunc (struct XYZ *p);
      

      这告诉编译器你正在引用一个类型struct XYZ,可能还没有声明,你正在传递一个指针。这允许编译器在不知道这个结构实际上由什么组成的情况下执行类型检查。

      注意指针的大小都是一样的,所以没有检查指针本身,只检查它所指向的东西。

      另请参阅使用typedef 解释的其他解决方案。

      【讨论】:

      • "C 支持的唯一“前向声明”和引用是指针"并非完全正确,因为您也可以(“前向") 声明struct XYZ;;
      【解决方案5】:

      您混淆了前向声明和类型定义。两者都是不同的东西。如果您想使用与前向声明的结构相同的用法,请执行以下操作:

      struct Foo;             // forward declaration
      struct Bar { int x; };  // definition
      
      void foo(struct Foo);   // works for a prototype
      void bar(struct Bar);   // works for a prototype
      

      您不能转发声明类型定义。也许这就是名称​​定义的原因。但是,您可以键入定义前向声明:

      struct Foo;                // forward declaration
      typedef struct Foo Foo_t;  // type definition of a forward declaration
      struct Bar { int x; };     // definition
      typedef struct Bar Bar_t;  // type definition of a definition
      
      void foo(Foo_t);           // works
      void bar(Bar_t);           // works
      

      如果您有一个函数定义,则必须完成类型。这意味着您已经在此位置定义了前向声明的类型,或者您必须使用指针。再说一遍:使用结构还是类型定义都没有关系。

      顺便说一句:正如您在我的示例中看到的那样,不完整的结构类型必须“通过引用”传递,这是一个都市传说。您可以在函数声明(原型)中使用不完整类型。但是,当您定义函数时,类型必须完成。在大多数情况下,这不是交易。

      【讨论】:

      • 我知道,但这是一个示例,出于教学目的,最好在结构标识符和其上的类型定义之间建立牢固的联系。正如您在 Q 中看到的那样,我将其拆分只是为了证明结构声明/定义与类型定义不同。
      • 比一个简单的_s 后缀更有效,而不是教一个不好的做法。非常适合你,你知道,不要用糟糕的例子绊倒别人。
      • 我不知道,sypedef 是什么。顺便说一句:使用FooBar 作为标识符也很糟糕。
      • _s 用于structFooBar 是说明性的,_t 不是。如果你想坚持让你的榜样很差,那是你的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 2012-06-09
      • 1970-01-01
      • 2012-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多