87

我查看了一些代码并注意到约定是将指针类型转换为

SomeStruct* 

进入

typedef SomeStruct* pSomeStruct;

这有什么优点吗?

4

15 回答 15

113

当指针本身可以被视为一个“黑匣子”,即其内部表示应该与代码无关的一段数据时,这可能是合适的。

本质上,如果您的代码永远不会取消引用指针,而您只是将它传递给 API 函数(有时通过引用),那么 typedef 不仅会减少*代码中 s 的数量,而且还会向程序员建议指针应该真的不要被插手。

如果需要,这也使得将来更改 API 变得更容易。例如,如果您更改为使用 ID 而不是指针(反之亦然),现有代码不会中断,因为从一开始就不应该取消引用指针。

于 2009-04-15T03:28:56.093 回答
81

不是我的经验。隐藏 ' *' 会使代码难以阅读。

于 2009-04-15T03:17:01.770 回答
28

我在 typedef 中使用指针的唯一一次是在处理指向函数的指针时:

typedef void (*SigCatcher(int, void (*)(int)))(int);

typedef void (*SigCatcher)(int);

SigCatcher old = signal(SIGINT, SIG_IGN);

否则,我发现它们比有用更令人困惑。


删除的声明是指向signal()函数的指针的正确类型,而不是信号捕获器的指针。可以通过以下方式使其更清晰(使用SigCatcher上面更正的类型):

 typedef SigCatcher (*SignalFunction)(int, SigCatcher);

或者,声明signal()函数:

 extern SigCatcher signal(int, SigCatcher);

也就是说, aSignalFunction是一个指向函数的指针,该函数接受两个参数(anint和 a SigCatcher)并返回 a SigCatchersignal()它本身是一个函数,它接受两个参数(an和inta SigCatcher)并返回 a SigCatcher

于 2009-04-15T03:34:19.920 回答
16

这可以帮助您避免一些错误。例如在以下代码中:

int* pointer1, pointer2;

pointer2 不是int *,它是简单的int。但是使用 typedefs 这不会发生:

typedef int* pInt;
pInt pointer1, pointer2;

他们现在都是int *

于 2009-04-15T03:52:14.947 回答
16

我的回答是明确的“不”。

为什么?

好吧,首先,您只需将一个字符换成*另一个字符p。那是收益。仅此一项就可以阻止您这样做,因为做多余的毫无意义的事情总是不好的。

其次,这也是重要的原因,不好隐藏*携带意义。如果我将某些东西传递给这样的函数

void foo(SomeType bar);

void baz() {
    SomeType myBar = getSomeType();
    foo(myBar);
}

我不希望myBar通过将其传递给foo(). 毕竟,我是按价值传递的,所以foo()只能看到副本myBar对吗?不是 whenSomeType被别名为某种指针!尤其是当该指针充当对某种对象的引用时,该对象的值基本上就是指针的含义:我不在乎指针本身不会改变(由于按值传递),我感兴趣的是对象是否改变(指针后面的对象)。

这适用于 C 指针和 C++ 智能指针:如果您隐藏它们是指向您的用户的指针这一事实,您将造成完全不必要的混淆。所以,请不要给你的指针起别名。

(我相信 typedefing 指针类型的习惯只是一种错误的尝试,试图隐藏一个程序员有多少颗星http://wiki.c2.com/?ThreeStarProgrammer。)

于 2017-04-09T21:40:45.200 回答
5

这是风格问题。您在 Windows 头文件中经常看到这种代码。尽管他们更喜欢全大写版本,而不是以小写 p 为前缀。

我个人避免使用 typedef。让用户明确表示他们想要 Foo* 比 PFoo 要清楚得多。如今,Typedef 最适合使 STL 可读:)

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
于 2009-04-15T03:19:51.747 回答
5

它(就像很多答案一样)取决于。

在 C 中,这很常见,因为您试图将对象伪装成指针。您试图暗示这是您所有函数操作的对象(我们知道它是下面的指针,但它代表您正在操作的对象)。

MYDB   db = MYDBcreateDB("Plop://djdjdjjdjd");

MYDBDoSomthingWithDB(db,5,6,7);
CallLocalFuc(db); // if db is not a pointer things could be complicated.
MYDBdestroyDB(db);

MYDB 下面可能是某个对象的指针。

在 C++ 中,这不再是必需的。
主要是因为我们可以通过引用传递东西,并且方法被合并到类声明中。

MyDB   db("Plop://djdjdjjdjd");

db.DoSomthingWithDB(5,6,7);
CallLocalFuc(db);   // This time we can call be reference.
db.destroyDB();     // Or let the destructor handle it.
于 2009-04-15T04:45:15.753 回答
5

假设感兴趣的语言是 C 进行讨论。尚未考虑 C++ 的分支。

对未标记的结构使用 aa 指针 typedef

定义为指针的结构的大小问题引发了关于使用typedeffor (结构) 指针的有趣的侧面光。

考虑无标记的具体(非不透明)结构类型定义:

typedef struct { int field1; double field2; } *Information;

成员的细节与本次讨论完全无关;重要的是这不是一个不透明的类型typedef struct tag *tag;(你不能通过没有typedef标签的 a 来定义这种不透明的类型)。

提出的问题是“你怎么能找到那个结构的大小”?

简短的回答是“仅通过类型的变量”。没有与 . 一起使用的标签sizeof(struct tag)sizeof(*Information)例如,您不能有用地编写andsizeof(Information *)是指向指针类型的指针的大小,而不是结构类型的大小。

事实上,如果你想分配这样的结构,除了通过动态分配(或模拟动态分配的代理技术)之外,你无法创建一个。没有办法创建指针被调用的结构类型的局部变量,也没有办法创建结构类型的Information文件范围(全局或static)变量,也没有办法嵌入这样的结构(如与指向此类结构的指针相反)转换为另一个结构或联合类型。

你可以——必须——写:

Information info = malloc(sizeof(*info));

除了指针隐藏在 中之外typedef,这是一个很好的做法——如果类型发生info变化,大小分配将保持准确。但在这种情况下,它也是获取结构体大小和分配结构体的唯一方法。并且没有其他方法可以创建结构的实例。

这有害吗?

这取决于你的目标。

这不是一个不透明的类型——结构的细节必须在指针类型为typedef'd 时定义。

它是一种只能用于动态内存分配的类型。

这是一种无名的类型。指向结构类型的指针有名称,但结构类型本身没有。

如果您想强制执行动态分配,这似乎是一种方法。

不过,总的来说,它更容易引起混乱和焦虑,而不是启蒙。

概括

通常,使用typedef定义指向无标记结构类型的指针是一个坏主意。

于 2017-04-09T19:52:09.890 回答
5

不。

混在一起的那一刻,它会让你的生活变得悲惨const

typedef foo *fooptr;
const fooptr bar1;
const foo *bar2

bar1bar2是同一类型吗?

是的,我只是在引用 Herb Sutter 的 Guru。她说了很多实话。;)

- 编辑 -

添加引用文章的链接。

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

于 2019-01-16T08:57:29.897 回答
4

typedef 用于使代码更具可读性,但是将指针作为 typedef 会增加混乱。最好避免使用 typedef 指针。

于 2009-04-15T03:55:05.813 回答
3

如果这样做,您将无法创建 const pSomeStruct 的 STL 容器,因为编译器会读取:

list<const pSomeStruct> structs;

作为

list<SomeStruct * const> structs;

这不是合法的 STL 容器,因为元素不可分配。

看到这个问题

于 2010-01-27T17:24:52.343 回答
1

Win32 API 对几乎所有结构(如果不是全部)都执行此操作

POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2

它的一致性很好,但在我看来它并没有增加任何优雅。

于 2009-04-15T03:42:21.413 回答
1

typedef 的目的是隐藏实现细节,但是 typedef 指针属性隐藏太多,使代码更难阅读/理解。所以请不要那样做。


如果你想隐藏实现细节(这通常是一件好事),不要隐藏指针部分。以标准FILE接口的原型为例:

FILE *fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, FILE *stream);

这里 fopen 返回一个指向某个结构的指针FILE(您不知道其实现细节)。也许FILE不是一个很好的例子,因为在这种情况下,它可以与一些隐藏它是指针的事实的 pFILE 类型一起工作。

pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);

但是,这只会起作用,因为您永远不会弄乱直接指向的内容。根据我的经验,当您键入一些指针时,您在某些地方修改代码变得非常难以阅读。

于 2009-04-15T11:56:20.197 回答
0

前段时间,我会对这个问题回答“不”。现在,随着智能指针的兴起,指针不再总是用星号'*'定义。因此,类型是否是指针没有什么明显的。

所以现在我想说:typedef 指针很好,只要非常清楚它是一个“指针类型”。这意味着您必须专门为它使用前缀/后缀。不,例如,“p”不是一个足够的前缀。我可能会选择“ptr”。

于 2009-04-15T05:55:29.847 回答
0

一般来说,没有。在特定情况下,是的。

有一些其他答案暗示了一些结构,那就是指针类型。我想到了几个仅指针类型的构造。如果有人想到更多,我会将它们添加到列表中。

不透明类型

这些类型的实现对用户完全隐藏。您通常会在标头中看到一个结构 typedef,但没有该结构的实现。因此,您不能取消引用这些类型的值。对这种类型进行操作的所有函数都采用指向这些类型的指针。在这些情况下添加指向 typedef 的指针是合适的。您经常会看到这些称为“句柄”的类型。

typedef struct handle_ * handle;

handle get_handle(string s);
void mutate_handle(handle h, value v);
void release_handle(handle h);

灵活的数组成员类型

另一种仅指针类型是使用灵活数组成员 (FAM) 定义的类型。FAM 类型中的最后一个成员是不受约束的数组。您打算为这些类型动态分配存储空间,并且灵活数组被视为与结构内联。您可以访问 FAM 类型中的字段,但不能取消引用整个对象。在这里添加指向 typedef 的指针也是合适的。

typedef struct string {
    size_t capacity;
    size_t length;
    char buffer[];
} * string;

string string_new(void);
size_t string_length(string s);
void string_append(string * s, char c);
void string_delete(string * s);
于 2021-11-30T21:58:53.223 回答