是否可以在 C 中始终引用前向声明和非前向声明的结构?

Is it possible to consistently refer to forward-declared and non-forward-declared structs in C?

这不适用于 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;

声明一个名为 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");
}

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

Is there a way to declare types in C such that all function prototypes can consistently always refer to custom types in the same way, regardless of whether they are forward-declared or not?

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);
}

当定义和声明分散在不同的翻译单元之间时,这就更清楚了,但是上面必须这样做。

所以我向您建议的是,在将结构用于函数原型(通过指针或值)之前,您始终在单独的行中转发声明它们。并且在真正需要之前不要引入完整的定义。


1 关于 fa.linux.kernel 的一封可爱的信件,其中 Linus Torvalds 比我清楚地阐述了为什么你应该更喜欢使用完整的结构标签。

您的问题不是前向声明与非前向声明,而是 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

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

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

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

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

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

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

C唯一支持的"forward declaration"和引用是指针,例如:

void myFunc (struct XYZ *p);

这告诉编译器您正在引用一个类型 struct XYZ,可能尚未声明,您正在传递一个指针。这允许编译器在不知道这个结构实际包含什么的情况下执行类型检查。

请注意,指针的大小都是相同的,因此不检查指针本身,只检查它指向的对象。

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