0

我有一个标题B,其中包含 A标题使用B的类型以及其中A使用的类型。所以,如果我包含BA使用B's类型编译中就可以了。但是,如果我确实包含AinB我会有一个循环依赖,但是所有使用include guards它的方式都不会发生,但是B当我尝试使用A's类型时,我在标题中得到一个错误:编译器不知道它们,导致undefined identifier编译器错误。我希望这很清楚。

编辑: 有问题的类型是struct和创建的,typedef例如,

一个文件:

struct foo {
struct baa *b; // <- this struct is defined in B file
};

B文件:

struct baa {
//etc..
};

void f(struct foo* f); // this struct is defined in A
4

2 回答 2

2

解决方案取决于您如何使用类型 A 和 B——最好的办法是使用前向声明。

在文本中描述的示例在 B 中创建 A 类型的字段,然后还将 B 作为 A 中的字段使用是无法解决的 - 您将需要使用如下示例所示的指针

您实际上作为代码提供的示例,您只需要在声明“foo”之前包含 Bh - 它可能只是您的包含守卫出错了

对于指针示例;

struct B; // forward declaration
struct A {
    struct B* my_b;
};

乙:

struct A; // forward declaration
struct B {
    struct A* my_a;
};
于 2013-11-03T01:46:08.110 回答
1

如果您不使用 C11,则必须确保 typedef 只出现一次。对代码影响最小的解决该问题的方法是struct tag对所有结构类型使用该符号。这是Linux 内核编码风格指南所要求的。

您还需要通过指针传递(大多数)结构。

所以,你可能有:

文件A.h

#ifndef FILEA_H_INCLUDED
#define FILEA_H_INCLUDED

struct tag_from_fileA;
struct tag_from_fileB;

extern int alternative_compound(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2);

#endif

文件B.h

#ifndef FILEB_H_INCLUDED
#define FILEB_H_INCLUDED

struct tag_from_fileA;
struct tag_from_fileB;

extern int compound_function(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2);

#endif

现在,这些文件中的每一个都可以单独使用,也可以联合使用。在实现代码中,您可能有:

文件A.c

#include "fileA.h"
#include "fileB.h"

struct tag_from_fileA { int x; double y; char z[32]; };

int alternative_compound(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2)
{
    ...code here can access internals of arg1...
    ...but it cannot access the internals of arg2...
}

文件B.c

#include "fileB.h"
#include "fileA.h"

struct tag_from_fileB { char *str1; char *str2; char *str3; };

int compound_function(struct tag_from_fileA *arg1, struct tag_from_fileB *arg2)
{
    ...code here can access internals of arg2...
    ...but it cannot access the internals of arg1...
}

现在,如果其中一个fileA.cfileB.c必须访问另一个文件中定义的结构的内部,那么您可以将结构的定义放在标题中。实际上,您可以对两种结构都这样做。然后,您只需确保fileA.hhas#include "fileB.h"在顶部(在标头保护之后,但在需要结构类型的内部之前),并且fileB.h#include "fileA.h"顶部(和以前一样)。

这样,客户端文件 ( fileC.c) 可以包含其中一个fileA.hfileB.h(或两者)并获得所有需要的定义。

如果函数不需要实际的结构(所以你永远不会传递struct tag_from_fileA value,而只是传递struct tag_from_fileA *ptr),并且客户端文件永远不需要创建结构类型的实例(它们调用返回指向结构类型的指针的“构造函数”),并且客户端文件永远不需要在结构内部戳(你有'访问器'函数来完成这项工作,我的意思是函数,而不是宏或内联函数),那么你就不需要头文件中的结构定义。你有不透明的类型。这些都很好;它们有助于将代码与结构细节的变化隔离开来。如果源文件知道结构的内部结构,则必须在结构更改时重新编译它们。

处理typedef

如果你坚持使用typedef(我当然更喜欢它,虽然我承认 Linux Kernel 团队的智慧),那么使用 C89/90 和 C99,你必须确保任何个人typedef只出现一次。只有当你有两个像上面这样相互乱伦的标题时,你才会真正遇到麻烦。您可以使用宏骇客:

#ifndef TYPEDEF_TAG_FROM_FILEA
#define TYPEDEF_TAG_FROM_FILEA
typedef struct tag_from_fileA tag_from_fileA;
#endif

并将其包含在两个标题中(以及另一个结构标记的相应替代项)。fileA.h或者放置在每个and中包含的第三个标题中fileB.h(并在typedef需要 s 时使用)。如果您强制执行作为typedef struct tag tag;您允许的唯一形式的规则(而不是允许typedef struct sometag SomethingUnrelated;,您可以使用该表示法编写原型,struct tag但使用该表示法编写代码tag。不过,这一切都会变得有点复杂。

C11 允许你重复typedefs;C99 没有。

ISO/IEC 9899:1999 §6.7 声明

¶3 如果标识符没有链接,除了 6.7.2.3 中指定的标签外,在相同范围和相同名称空间中的标识符声明(在声明符或类型说明符中)不得超过一个。

ISO/IEC 9899:2011 §6.7 声明

¶3 如果标识符没有链接,则在相同范围和相同名称空间中的标识符声明(在声明符或类型说明符中)不得超过一个,但以下情况除外:

  • 可以重新定义 typedef 名称以表示与当前相同的类型,前提是该类型不是可变修改的类型;
  • 标签可以按照 6.7.2.3 中的规定重新声明。

因此,如果您可以可靠地使用 C11 语法,您可以typedef根据需要简单地编写 s 。不过,您仍然必须确保结构体本身只有一个定义。

您可以在许多其他问题中找到有关标头及其编写方式和共享类型定义的信息,包括:

于 2013-11-03T02:00:35.630 回答