命名空间冲突
无论您做什么,都应该将结构标记从_C8B10 更改为不会践踏为实现保留的命名空间的内容。
ISO/IEC 9899:2011
7.1.3 保留标识符
¶1 每个标头声明或定义其相关子条款中列出的所有标识符,并且
可选地声明或定义在其关联的未来库方向中列出的标识符
始终保留用于任何用途或用作文件的子条款和标识符
范围标识符。
— 以下划线和大写字母或其他字母开头的所有标识符
下划线始终保留用于任何用途。
— 所有以下划线开头的标识符始终保留用作标识符
在普通名称空间和标记名称空间中都具有文件范围。
以下划线开头的名称有风险;它们是为系统库的提供者而存在的。显然,如果您的库是实现的一部分,那么这不适用于您,但我认为如果是这种情况,您不太可能会问这个问题。
如果是我的代码,我会直接去掉下划线:typedef struct C8B10 { ... } C8B10; 很好(typedef 名称在普通标识符命名空间中,结构标签在标签命名空间中,两者不冲突) .
头文件组织
您的图表与大多数人通常编写此类图表的方式相反。源代码包括标题,所以它可能被图解为:
main.c
main.h
child1.c
child1.h
child2.c
child2.h
然而,这不可能是全部。您创建一个标头以在源文件之间共享声明;仅包含在一个文件中的标头并非绝对必要(尽管创建此类文件可能有正当理由)。文件main.c 使用child1.c 中定义的一些函数(和类型——可能是宏甚至全局变量,别想了),或者child1.c 中的代码使用main.c 中的材料(或者可能两者都使用) )。因此,main.c 应包含 child1.h 或 child1.c 应包含 main.h 或两者兼而有之。
对于库,您需要考虑的另一个方面是“库的客户将如何使用代码?”这是至关重要的。客户端代码需要什么标头?客户端代码需要哪些功能?客户端代码是否需要任何类型定义?客户端代码是否需要访问 C8B10 结构的成员,还是只需将其视为不透明类型?
您的外部标头(库的客户使用的标头)应尽可能小,但自包含。假设外部标头是c8b10.h。如果客户端源代码将#include "c8b10.h" 作为源文件中的第一个或唯一标头,则标头中的代码应编译。如果你的接口使用size_t,例如,你需要在c8b10.h中#include <stddef.h>。
在您的情况下,我希望您的C8B10 结构应在main.h 中定义,并且child1.c 和child2.c 都应包含main.h。 main.c 很可能还应该包括 child1.h 和 child2.h。而main.h 应该包含外部c8b10.h 标头,所以实际上每个文件都包含它。
main.c
main.h
c8b10.h
child1.h
child2.h
child1.c
main.h
c8b10.h
child1.h
child2.c
main.h
c8b10.h
child2.h
child2.c是否需要包含child1.h,child1.c是否需要包含child2.h取决于代码的编写方式以及每个文件提供的哪些服务在哪里使用。
您可能会发现只有两个标头更明智——外部标头c8b10.h 和一个内部标头c8b10-private.h。 c8b10-private.h 标头将包含在库中的每个源文件中。它将包含的第一个标头是外部 c8b10.h 标头(以帮助自动检查 c8b10.h 标头是自包含的)。 c8b10-private.h 标头将对应于main.h、child1.h 和child2.h 的合并,减去c8b10.h 中定义的外部可访问内容。这导致:
main.c
c8b10-private.h
c8b10.h
child1.c
c8b10-private.h
c8b10.h
child2.c
c8b10-private.h
c8b10.h
要记住的关键点是标头用于源文件之间的通信。其余的大部分都是自动的。