考虑以下 C 代码:
typedef char * MYCHAR;
MYCHAR x;
我的理解是,结果将是 x 是“char”类型的指针。但是,如果 x 的声明发生在远离 typedef 命令的地方,代码的人类读者不会立即知道 x 是一个指针。或者,可以使用
typedef char MYCHAR;
MYCHAR *x;
哪个被认为是更好的形式?这不仅仅是风格问题吗?
如果指针永远不会被取消引用或以其他方式直接操作——IOW,你只将它作为参数传递给 API——那么可以将指针隐藏在 typedef 后面。
否则,最好明确类型的“指针”。
我只会在结果类型的指针性质无关紧要的情况下使用指针类型定义。例如,当一个人想要声明一个恰好被实现为指针但不应该被用户用作指针的不透明“句柄”类型时,指针 typedef 是合理的。
typedef struct HashTableImpl *HashTable;
/* 'struct HashTableImpl' is (or is supposed to be) an opaque type */
在上面的示例中,HashTable
是哈希表的“句柄”。用户最初将从CreateHashTable
函数接收该句柄并将其传递给HashInsert
函数等。用户不应该关心(甚至不知道)这HashTable
是一个指针。
但是在用户应该理解类型实际上是一个指针并且可以用作指针的情况下,指针类型定义会严重混淆代码。我会避开他们。显式声明指针使代码更具可读性。
有趣的是,C 标准库避免了这种指针类型定义。例如,FILE
显然打算用作不透明类型,这意味着库可以将其定义为,typedef FILE <some pointer type>
而不是让我们一直使用FILE *
。但出于某种原因,他们决定不这样做。
我不是特别喜欢 typedef 指向指针,但它有一个优点。当您在单个声明中声明多个指针变量时,它消除了混淆和常见错误。
typedef char *PSTR;
...
PSTR str1, str2, str3;
可以说比:
char *str1, str2, str3; // oops
我也认为这是风格/惯例的问题。在 Apple 的 Core Graphics 库中,他们经常“隐藏”指针并使用将“Ref”附加到类型末尾的约定。例如,CGImage *
对应于CGImageRef
。这样你仍然知道它是一个指针引用。
我更喜欢离开*
,它表明有一个指针。你的第二个例子应该缩短为char* x;
,这是没有意义的。
另一种看待它的方式是从类型的角度。类型定义了对该类型可能的操作,以及调用这些操作的语法。从这个角度来看,MYCHAR 就是这样。程序员有责任了解其上允许的操作。如果它像第一个示例一样声明,那么它支持 * 运算符。您始终可以适当地命名标识符以阐明其用途。
声明一个指针类型很有用的其他情况是参数的性质对用户(程序员)是不透明的。可能有 API 想要将指针返回给用户,并期望用户在其他时间将其传递回 API。像不透明的句柄或 cookie 一样,仅供 API 在内部使用。用户不关心参数的性质。通过在 API 中暴露 * 来避免混淆或暴露其确切性质是有意义的。
如果您查看几个现有的 API,看起来好像不将指针放入类型似乎更好:
FILE *
MYSQL *
返回的mysql_real_connect()
MYSQL *
MySQLmysql_store_result()
和返回的mysql_use_result()
可能还有很多其他人。
对于 API,不必将结构定义和指针隐藏在“抽象”类型定义后面。
/* This is part of the (hypothetical) WDBC- API
** It could be found in wdbc_api.h
** The struct connection and struct statement ar both incomplete types,
** but we are allowed to use pointers to incomplete types, as long as we don't
** dereference them.
*/
struct connection *wdbc_connect (char *connection_string);
int wdbc_disconnect (struct connection *con);
int wdbc_prepare (struct connection * con, char *statement);
int main(void)
{
struct connection *conn;
struct statement *stmt;
int rc;
conn = wdbc_connect( "host='localhost' database='pisbak' username='wild' password='plasser'" );
stmt = wdbc_prepare (conn, "Select id FROM users where name='wild'" );
rc = wdbc_disconnect (conn);
return 0;
}
上面的片段编译得很好。(但显然无法链接)
这不仅仅是风格问题吗?
是的。例如,这个:
typedef int *ip;
const ip p;
不一样:
const int *p; // p is non-const pointer to const int
它与以下内容相同:
int * const p; // p is constant pointer to non-const int
在这里阅读关于typedef 的const 怪异 typedef pointer const 怪异