可能是重复问题:
typedef指针好主意吗?
我在使用许多API时都看到了这种奇怪的用法:
typedef type_t *TYPE;
我的观点是声明一个类型为 TYPE
的变量并不会清楚地表明实际上是声明了一个指针。
你是否跟我一样认为这会带来很多混淆?这是为了强制封装,还是有其他原因?你认为这是一种不好的实践吗?
可能是重复问题:
typedef指针好主意吗?
我在使用许多API时都看到了这种奇怪的用法:
typedef type_t *TYPE;
我的观点是声明一个类型为 TYPE
的变量并不会清楚地表明实际上是声明了一个指针。
你是否跟我一样认为这会带来很多混淆?这是为了强制封装,还是有其他原因?你认为这是一种不好的实践吗?
一般来说,这是一个不好的做法。主要问题在于它与 const
不兼容:
typedef type_t *TYPE;
extern void set_type(TYPE t);
void foo(const TYPE mytype) {
set_type(mytype); // Error expected, but in fact compiles
}
foo()
的作者表达他们真正的意思,提供TYPE
的库也必须提供CONST_TYPE
。typedef const type_t *CONST_TYPE;
因此,一个经验法则:
对于结构体(特别是不完整的结构体),应该创建typedefs,而不是指向这些结构体的指针。
如果底层结构的定义不希望公开(通常是值得赞扬的),那么封装应该由结构体不完整来提供,而不是通过不方便的typedefs:
这样 foo()
可以拥有签名 void foo(CONST_TYPE mytype)
,到这一步我们已经陷入了荒谬。
struct type_t;
typedef struct type_t type_t;
void set_type(type_t *);
int get_type_field(const type_t *);
const_pthread_t
,可以在其上调用 pthread_getschedparam
但不能调用 setschedparam
。当然,const pthread_t
无法达到这个目的,就像您的示例中 typedef 的指针无法实现一样。而 ftell
接受 FILE*
而不是 const FILE*
,虽然我认为它应该是 const 的,以支持对 FILE* 的 const 安全使用。因此,我认为在某些情况下,这个 const
问题很重要/关键,但并非总是如此。这是 C 的预先 const 遗留问题,也许是懒惰 :-) - Steve Jessopconst FILE*
进行了任何更新,但是...并没有。 (我还希望能帮助您理解FILE*
!:-))也许这与某些东西感觉像原子类型有关--人们也不会写const int
。或者是一种鸡生蛋蛋生鸡的问题:如果没有任何查询函数使用const FILE*
,那么为什么要编写自己的foo()
函数来使用它--您将如何使用参数? - John Marshallftell
不能接受const FILE *
,因为(在POSIX或任何其他实现中有线程的情况下),它必须在FILE
对象上获取互斥锁,除非可以使用单个原子操作获取当前位置。const FILE *
的整个概念基本上是无意义的;它不能用于任何事情。 - R.. GitHub STOP HELPING ICE一种常见的习惯用法是在类型后缀加上_p
,以指示它是一个指针,同时保留指针特性。
有时候必须仅使用指针类型,如果指向的结构体不是公开可用的。这有助于实现数据隐藏。例如:
typedef struct hidden_secret_object * object;
void change_object(object foo);
hidden_secret_object
的结构,而不会破坏外部代码。typedef struct h_s_o object; void change_object(object *foo);
这样做可以实现同样的效果。这个例子与问题看起来不相关。 - schot我也觉得不够清晰。同样,我也不喜欢全大写的类型(我尝试将其保留给 #define)。
这种方式会让人误以为它实际上是值类型,而我们正在谈论指针类型。指针类型可以完全用智能指针来抽象掉,但在C语言中并不常见。
像之前提到的一样,在末尾加上 _p、_ptr、Pointer 或任何类似的内容可以增加清晰度;虽然会增加输入量,但可以防止你因为低级错误(例如使用 '.' 而不是 '->')浪费宝贵的开发时间。
有时候它会咬我一口:
for (vector<typedef_name_that_doesnt_indicate_pointerness_at_all>::iterator it;
it != v.end(); ++it)
{
it->foo(); // should have been written (*it)->foo();
}
如果它是指向不完整类型的指针,或者由于其他原因用户不需要对其进行解引用,我认为这并不是不良实践。我从来没有理解过FILE*
。
如果您正在进行多级间接引用,并且希望在某些情况下使用它而忽略其中一些级别,则我也认为这不是不良实践。typedef char **argarray
之类的东西。
如果用户需要对其进行解引用,则在C中,保留*
可能是最好的选择。在C++中,人们习惯于具有重载operator*
的用户定义类型,例如迭代器。在C中,这并不正常。
存储类限定符(如'const')在typedef指针和“自然”指针中的工作方式不同。虽然这对于'const'通常不是一件好事,但对于编译器特定的存储类(如“xdata”)非常有用。例如:
xdata WOKKA *foo;
将声明“foo”为指针,存储在默认存储类中,在xdata中指向WOKKA。而声明:
xdata WOKKA_PTR bar;
将声明“bar”为指针,存储在xdata中,指向WOKKA_PTR中指定的任何存储类中的WOKKA。如果库例程期望指向具有特定存储类的内容的指针,则在指针类型中定义这些存储类可能会很有用。
type_ptr
或类似的名称: typedef type_t* type_ptr;
const
无用的交互 - 而且这个答案排名较低。在我看来,const
问题是这些typedef的最大问题,因此并不是重复的问题。 - John Marshallint* i
而不是PINT = 我想Windows的BASIC遗产使他们试图隐藏指针。其次,全大写看起来很可怕。我实际上从未费心去使用win32 api,因为它太乱了。 - W.K.S