如果您不使用 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.c
或fileB.c
必须访问另一个文件中定义的结构的内部,那么您可以将结构的定义放在标题中。实际上,您可以对两种结构都这样做。然后,您只需确保fileA.h
has#include "fileB.h"
在顶部(在标头保护之后,但在需要结构类型的内部之前),并且fileB.h
在#include "fileA.h"
顶部(和以前一样)。
这样,客户端文件 ( fileC.c
) 可以包含其中一个fileA.h
或fileB.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 允许你重复typedef
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 语法,您可以typedef
根据需要简单地编写 s 。不过,您仍然必须确保结构体本身只有一个定义。
您可以在许多其他问题中找到有关标头及其编写方式和共享类型定义的信息,包括: