如何在一个头中声明一个结构,以供 c 中的多个文件使用?

如果我有一个具有 struct 的 source.c 文件:

struct a {
int i;
struct b {
int j;
}
};

如何在另一个文件(即 func.c)中使用这个结构?

我是否应该创建一个新的头文件,在那里声明结构,并在 func.c中包含该头文件?

还是应该在头文件中定义整个结构,并在 source.cfunc.c中包含它?如何在两个文件中声明结构 extern

我应该 typedef它吗? 如果是,如何?

345034 次浏览

啊哈:

#ifndef A_H
#define A_H


struct a {
int i;
struct b {
int j;
}
};


#endif

好了,现在你只需要把 a.h 包含到你想要使用这个结构的文件中。

对于跨多个源文件使用的结构定义,您一定要将其放在一个头文件中。然后将该头文件包含在需要该结构的任何源文件中。

extern声明不用于结构定义,而是用于变量声明(即定义了结构类型的一些数据值)。如果希望在多个源文件中使用相同的变量,请在头文件中将其声明为 extern,如下所示:

extern struct a myAValue;

然后,在 源文件中定义实际的变量:

struct a myAValue;

如果您忘记这样做,或者不小心在两个源文件中定义了它,链接器会让您知道这一点。

如果这个结构要被其他的文件 fun.c 使用,怎么做呢?

当一个类型在一个文件中使用时(比如 fun.c 文件) ,它必须是可见的。最糟糕的方法是复制粘贴到每个源文件需要它。

正确的方法是将它放在一个头文件中,并在需要时包含这个头文件。

我们是否应该打开一个新的头文件并在其中声明结构,并将该头文件包含在 fun.c 中?

这是我更喜欢的解决方案,因为它使代码高度模块化:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME


struct a
{
int i;
struct b
{
int j;
}
};


#endif

我会把使用这个结构的函数放在同一个标题中(这个函数是它的“接口”的“语义”部分)。

通常,我可以用结构名称命名文件,然后再次使用该名称来选择头保护定义。

如果需要使用指向结构的指针声明函数,则不需要完整的结构定义。简单的前向声明如下:

struct a ;

就足够了,而且它减少了耦合。

或者我们可以在头文件中定义总体结构,并将其包含在 source.c 和 fun.c 中吗?

这是另一种方法,稍微容易一些,但是模块化程度较低: 一些只需要结构就可以工作的代码仍然必须包含所有类型。

在 C + + 中,这可能会导致有趣的复杂性,但是这与主题无关(没有 C + + 标记) ,因此我不会详细说明。

那么如何在两个文件中都声明这个结构为 extern?

也许我不明白这一点,但是格雷格 · 休吉尔在他的文章 如何在一个头中声明一个结构,以供 c 中的多个文件使用?中给出了一个很好的答案。

那么我们应该如何打字呢?

  • 如果你正在使用 C + + ,不要。
  • 如果你使用 C,你应该。

原因是 C 结构管理可能很麻烦: 必须在所有使用的地方声明 struct 关键字:

struct MyStruct ; /* Forward declaration */


struct MyStruct
{
/* etc. */
} ;


void doSomething(struct MyStruct * p) /* parameter */
{
struct MyStruct a ; /* variable */
/* etc */
}

而 typedef 将使您能够在不使用 struct 关键字的情况下编写它。

struct MyStructTag ; /* Forward declaration */


typedef struct MyStructTag
{
/* etc. */
} MyStruct ;


void doSomething(MyStruct * p) /* parameter */
{
MyStruct a ; /* variable */
/* etc */
}

这是 很重要,你仍然保留一个结构的名称。写:

typedef struct
{
/* etc. */
} MyStruct ;

将只创建一个具有 typedef 名称的匿名结构,并且您将无法转发-声明它。因此,请遵循以下格式:

typedef struct MyStructTag
{
/* etc. */
} MyStruct ;

因此,您可以在任何地方使用 MyStruct,以避免添加 struct 关键字,并且在 typedef 不能工作时仍然使用 MystructTag (即前向声明)

编辑:

纠正了关于 C99结构声明的错误假设,乔纳森 · 莱弗勒正确地指出了这一点。

编辑2018-06-01:

Craig Barnes 在他的注释中提醒我们,您不需要为 struct“ tag”名称和它的“ typedef”名称保留单独的名称,就像我上面为了清晰起见所做的那样。

事实上,上面的代码可以写成:

typedef struct MyStruct
{
/* etc. */
} MyStruct ;

IIRC,这实际上是 C + + 在幕后用它更简单的结构声明所做的,以保持它与 C 的兼容性:

// C++ explicit declaration by the user
struct MyStruct
{
/* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

回到 C 语言,我已经看到了两种用法(不同的名称和相同的名称) ,没有一种有我知道的缺点,所以如果不使用 C 为结构和其他符号分隔“名称空间”,使用相同的名称会使阅读更简单。