我会要求 API 用户键入“*”,即 typedef 结构,而不是指向结构的指针。这是 GLib 中广泛使用的样式,它是最流行的 C 堆栈之一。
效果很好,因为知道您是否拥有指向结构或结构本身的指针很重要。例如,您可以将 NULL 存储在类型的变量中吗?如果你隐藏一个对象是一个指针,你必须创建一个特殊的 NIL_NODE 或其他值来替换 NULL。如果你只是把它作为一个指针,那么人们可以把它当作一个指针来对待。
另一个重要的好处是能够将实际的结构定义放在私有的地方,即在 .c 文件中而不是在头文件中。您可以只将 typedef 放在标头中,同时保持结构本身对 API 用户不透明。这要求人们仅使用您提供的导出方法来操作结构。这也意味着如果您更改结构字段,您不必重建世界。
选择这条路的另一个原因是,有时你确实想要一个可以复制的结构,但你仍然想对它进行 typedef。拿一个像 GdkPoint 这样的东西:
typedef struct {
int x, y;
} GdkPoint;
这里允许直接访问结构体很有用,例如:
GdkPoint point = { 10, 10 };
GdkPoint copy = point;
do_stuff_with_point(©);
但是,如果您的约定是“GdkPoint” typedef 将是一个指针,那么您将不得不不一致。
如果你能分辨出什么是指针,什么不是,代码会更清晰,如果你不将结构定义放在标题中,代码会更好地封装(对于表示抽象、不透明数据类型的结构,这可能是最常见的一种)。
我的 C 数据类型的默认模板在标题中是这样的:
typedef struct MyType MyType;
MyType* my_type_new(void);
void my_type_unref(MyType *t);
void my_type_ref(MyType *t);
然后是 .c 文件中的实际“struct MyType”,以及 new、unref 和对该类型的任何操作。
点、矩形、颜色等类型是“普通旧数据”并且需要直接字段访问的类型除外;如果不需要引用计数,另一种变化是使用 my_type_free() 而不是 _unref()。
无论如何,这是一种风格,基本上是 GLib 风格,它被广泛使用并且众所周知。