解决这种循环依赖

时间:2013-11-03 01:42:40

标签: c header-files

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

修改 相关类型为struct和使用typedef创建的类型,例如

A 文件:

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

2 个答案:

答案 0 :(得分:2)

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

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

你实际给出的示例,你只需要在声明“foo”之前加入B.h - 它可能只是你的包含警卫出了问题

对于指针示例;

A.H

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

B.h:

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

答案 1 :(得分:1)

如果你没有使用C11,你必须确保typedef只出现一次。围绕该问题的方法对代码的影响最小是对所有结构类型使用struct tag表示法。这是Linux Kernel Coding Style Guide要求的内容。

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

所以,你可能有:

fileA.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

fileB.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

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

fileA.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...
}

fileB.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.h在顶部有#include "fileB.h"(在标头保护之后,但在需要结构类型的内部之前),fileB.h具有{{1}顶部(如前所述)。

这样,客户端文件(#include "fileA.h")可以包含fileC.cfileA.h(或两者),并获取所需的所有定义。

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

处理struct tag_from_fileA *ptr

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

typedef

并在两个标头中包含它(以及其他结构标记的相应替代)。或者放置在每个typedef#ifndef TYPEDEF_TAG_FROM_FILEA #define TYPEDEF_TAG_FROM_FILEA typedef struct tag_from_fileA tag_from_fileA; #endif 中包含的第三个标题中(并且只要需要fileA.h时使用)。如果您强制执行fileB.h是您允许的唯一表单(而不是允许typedef)的规则,则可以使用typedef struct tag tag;表示法编写原型,但使用{{{}编写代码但是,这一切都有点令人费解。

C11允许你重复typedef struct sometag SomethingUnrelated; s; 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语法,则可以根据需要编写struct tag。但是,您仍然必须确保结构体本身只有一个定义。

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