【问题标题】:How to declare a structure in a header that is to be used by multiple files in c?如何在头文件中声明要由c中的多个文件使用的结构?
【发布时间】:2010-09-18 17:24:56
【问题描述】:

如果我有一个带有结构的 source.c 文件:

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

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

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

或者我应该在头文件中定义整个结构并将其包含在source.cfunc.c 中?如何在两个文件中声明结构extern

我应该typedef 吗?如果有,怎么做?

【问题讨论】:

  • 请注意,结构定义无效 C。struct b 的大括号后至少应该有一个分号,但是你的结构 a 声明了一个未使用的类型(你应该可能定义一个成员名称,可能是k,在内大括号之后和分号之前。

标签: c file header structure


【解决方案1】:

如果这个结构要被其他文件func.c使用怎么办?

当一个类型用于文件(即 func.c 文件)时,它必须是可见的。最糟糕的方法是将其复制粘贴到每个需要它的源文件中。

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

我们是否应该打开一个新的头文件并在那里声明结构并将该头文件包含在 func.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 和 func.c 中吗?

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

在 C++ 中,这可能会导致一些有趣的复杂情况,但这是题外话(没有 C++ 标签),所以我不会详细说明。

然后如何在两个文件中将该结构声明为 extern。 ?

也许我没看懂重点,但 Greg Hewgill 在他的帖子How to declare a structure in a header that is to be used by multiple files in c? 中有一个很好的答案。

我们应该如何 typedef 呢?

  • 如果您使用的是 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 ;

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

编辑:

更正了关于 C99 结构声明的错误假设,正如 Jonathan Leffler 正确指出的那样。

编辑 2018-06-01:

Craig Barnes 在他的评论中提醒我们,你不需要为结构“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 separate "namespaces" for structs and other symbols,使用相同的名称会使阅读更简单。

【讨论】:

  • 您能否评论或指出 C99 标准中保证您的“如果您使用 C99,请不要”评论的部分?
  • 你是对的。我最近测试了一个 C99,惊讶地发现我的 untypedefed 结构在使用 C++ 方式时无法识别。我搜索了编译器选项,然后,在所有我可以动手的标准文档中,但没有找到任何可以解释的东西,我相信这是可能的......
  • ... 无论如何,谢谢你的评论。我现在更正了。
  • struct 标记和typedef 名称无需使用不同的名称。 C 对 struct 标记使用不同的命名空间,因此您可以对两者都使用 MyStruct
  • @CraigBarnes 你是对的,但我希望通过阅读代码来清楚这一点。如果我给出了相同的名称,可能会让 C 新手感到困惑,因为需要在同一个“声明”中写上名称 *twice"。我将添加一个备注提及您的评论。谢谢!
【解决方案2】:

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

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

extern struct a myAValue;

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

struct a myAValue;

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

【讨论】:

  • 您可能会收到链接器错误,也可能不会。在 C 中,链接模型允许“通用”定义,因此没有初始化器(并且可能具有相同初始化器)的多个定义可能会起作用。这是一个“通用扩展”。我相信有些编译器也支持暂定(缺失)定义。
  • 然后,在一个源文件中,定义实际变量: ; ...或不小心在两个源文件中定义它... :)
  • 我用于声明/定义问题的一种技术是有条件地将GLOBAL 定义为extern 或在标题顶部什么都不定义,然后将变量声明为GLOBAL struct a myAValue;。从大多数源文件中,您安排使用 #define GLOBAL extern 版本(声明变量),并且从一个源文件中它会导致使用空定义,因此变量是 定义的.
  • 结构名称可以与 C 中的 typedef 名称相同,但不能在 C++ 中。
【解决方案3】:

啊哈:

#ifndef A_H
#define A_H

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

#endif

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

【讨论】:

    猜你喜欢
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    相关资源
    最近更新 更多