出于这个问题,请忽略与相互依赖性有关的软件设计问题,而应着重于技术方面。
请考虑两个头文件a.h
和b.h
。文件a.h
定义了b.h
所需的某些结构。但是,b.h
还定义了a.h
所需的结构。
据我了解,解决此循环#include
依赖关系的常用技术位于b.h
中,而不是包含a.h
,它将从{中声明一个结构定义。 {1}}。看起来像这样:
a.h
a.h
B.h
// No nice typedef
struct MyThing_t {
// ...
};
据我了解,这应该可以编译并工作。
但是-为什么有效?编译器在编译// Other things omitted
typedef struct MyThing_t MyThing;
// ... proceed using MyThing
时如何知道名称MyThing_t
?这是什么机制?
还-为什么我不应该在b.h
中也包含typedef
,以供使用a.h
没有循环依赖关系的模块使用?
答案 0 :(得分:2)
处理这种情况的最直接方法是在不同的标头中定义结构,例如basic.h
或类似的标头,并让a.h
和b.h
都包含该标头。因为在b.h
中
typedef struct MyThing_t MyThing
前向声明声明了一个 opaque 类型,该类型仅允许您使用MyThing*
指针。因为b.h
不知道结构的实际大小或内存布局。这实际上是一个非常常见的C习惯用法,因为它允许封装结构的内部结构,并允许在对该指针进行操作的函数的头文件中进行声明,而无需了解实现。
答案 1 :(得分:0)
它是typedef *struct* MyThing_t MyThing;
; struct Mything_t
不需要声明(这可能会导致错误,因此,对于所有内容使用typedefs是个好主意)。
我发现最好编写一个包含所有typedef的fwd.h
文件(或对于大型项目,每个目录),并将其包含在所有其他标头的开头。
偶然地,除非为POSIX工作,否则应避免使用以_t
结尾的名称。
答案 2 :(得分:0)
据我了解,这应该可以编译并工作。
是,不是。每个头都可以独立于源文件中的#include
d,但是B.h
不提供struct MyThing_t
的定义。在范围内没有定义的地方,这是一个不完整的类型,这极大地限制了您可以使用它进行的操作。
但是-为什么有效?编译器如何知道名称 MyThing_t何时编译b.h?
如果在到达struct MyThing_t
之前尚未意识到typedef
,则typedef
使其意识到。
这里的机制是什么?
带有标签的结构类型可以在没有事先声明或定义的情况下使用,但只能以不完整类型使用的方式使用。这就是允许结构包含指向其自身类型的对象的指针的原因。它通常可以用于声明指向所讨论类型的指针,也可以在typedef
中使用。另一方面,不完整的类型不能以任何需要知道其存储大小或(对于结构和联合类型)其成员的名称或类型的知识来使用。
也-为什么我不应该在a.h中也包含typedef,以供 使用a.h没有循环依赖关系的模块?
我不知道,你为什么不呢?这将比拆分它们更典型。不过,您确实需要注意两次声明相同的typedef
。在最新版本的C中这样做是合法的,但是C90和C99不允许这样做。