我最近在我的项目中使用了 Libxml2,并注意到它使用如下 typedef:
typedef struct _xmlNode xmlNode
typedef xmlNode * xmlNodePtr
第一个 typedef 的好处是显而易见的。但是我不确定为什么要为 xmlNode * 分配一个替代名称。对我来说,使用xmlNode *比使用xmlNodePtr更明确和可读,但我可能会遗漏一些东西。
这个 typedef 会解决什么问题,又会带来什么好处呢?
我最近在我的项目中使用了 Libxml2,并注意到它使用如下 typedef:
typedef struct _xmlNode xmlNode
typedef xmlNode * xmlNodePtr
第一个 typedef 的好处是显而易见的。但是我不确定为什么要为 xmlNode * 分配一个替代名称。对我来说,使用xmlNode *比使用xmlNodePtr更明确和可读,但我可能会遗漏一些东西。
这个 typedef 会解决什么问题,又会带来什么好处呢?
C API 通常提供不透明的句柄,这会阻止消费者询问它们是什么并试图戳进去。这些句柄是指针这一事实无关紧要,对消费者来说应该无关紧要,她也不应该为额外的星号带来的词汇混乱负担。
例如,完全可以通过定义句柄来编写 C++ 绑定:
typedef void * MyHandle;
现在我们给幸福的 C 消费者一些功能:
MyHandle create_gizmo();
void destroy_gizmo(MyHandle);
int do_magic(MyHandle, int, int);
就那么简单。用户立即看到如何使用它:
#include "MagicAPI.h"
MyHandle h = create_gizmo();
submit_result(do_magic(h, 12, 91));
destroy_gizmo(h);
C++ 库开发人员只需解开句柄并填充 API 函数(当然是声明的extern "C"
):
#include "MagicAPI.h"
#include "SuperGizmo.hpp"
MyHandle create_gizmo() { return static_cast<MyHandle>(new SuperGizmo); }
void destroy_gizmo(MyHandle h) { delete static_cast<SuperGizmo *>(h); }
int do_magic(MyHandle h, int a, int b)
{
return static_cast<SuperGizmo *>(h)->foo(a, b);
}
对于某些人来说,名称中带有“ptr”的 typedef 比使用普通指针语法的声明更具可读性和“自然性”。如果你喜欢写作
foo* p;
代替
foo *p;
那么指针 typedef 可能会吸引您,因为它专门避免了写入错误
foo* p, q;
当你的意思是
foo *p, *q;
相反,你可以写
fooptr p, q;
这个 typedef 会解决什么问题,又会带来什么好处呢?
我的观点是 typedefing 对象指针是不好的,不应该这样做。
首先,它通过隐藏声明的对象是指针类型来改变 C 语言的语法。
第二种类型限定符 ( const
and volatile
) 不能穿透 typedef。
如果我们以您的为例:
typedef struct _xmlNode xmlNode;
typedef xmlNode * xmlNodePtr;
现在无法声明对象,因此指针对象const
使用xmlNodePtr
别名。
const xmlNodePtr xp;
手段xp
不是。const
_*xp
const xmlNode x = /* ... */;
const xmlNodePtr xp = &x; // Error!
顺便说一句,在 Linux 内核编码风格中,他们还建议不要使用typedef
指针:
“将 typedef 用于结构和指针是错误的。”
有一些区别。
首先,如果您在一行中执行多个声明,则以下两个片段是等效的:
char *c1, *c2, *c3, *c4;
typedef char * charPtr;
charPtr c1, c2, c3, c4; // I don't need to repeat the unary * everywhere
这在一定程度上很有用,因为您可能会在尝试执行以下操作时遇到语义问题:
char * c1, c2, c3, c4; // declares one char pointer and three chars
其次,它们可以极大地简化函数指针类型的定义,这为几乎无限的丑陋和肮脏提供了机会。
float (*someCrazyFunction)(float(*)(), float) = foo; // WTF is this
typedef float (*crazyFunction)(float(*)(), float);
crazyFunction myCrazyFunction = foo; // easier to deal with