我浏览了一些代码,发现约定是将指针类型转为

SomeStruct* 


转换为

>这有什么好处吗?

评论

它在C语言中很常见。因为您试图隐藏它是指针的事实。这是您传递给库中所有接口的对象。在C ++中,虽然不是闻所未闻,但并不常见且不鼓励使用它。

关于此一般主题的有趣文章:对话:仲夏夜之疯狂另请参阅Linux内核编码样式,以获取“不要使用typedef指针(也不要为结构或联合提供typedef)”的极端观点。

#1 楼

当指针本身可以被视为“黑匣子”时,即内部数据与代码无关的一条数据时,这可能是适当的。

本质上,如果您的代码永远不会取消引用该指针,然后将其传递给API函数(有时通过引用),则typedef不仅会减少代码中*的数量,而且还建议程序员不要真正干预该指针。

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

评论


实际示例:在OS X中,CoreFoundation框架广泛使用了此技术,将它们称为“不透明”数据类型。

– hbw
09年4月15日在3:32

同样在OS X中:pthread_t是不透明的类型。类型定义为指向“结构_opaque_pthread_t”的指针,该结构本身是不透明的字节数组。

–亚当·罗森菲尔德
09年4月15日在4:42

反例:FILE * fp?实际上,我同意你的看法,但是有很多相反的先例。

–乔纳森·莱弗勒(Jonathan Leffler)
09年4月15日在4:56

'当然,如果执行此操作,则不会将不透明类型命名为“ pFoo”。

– MSalters
09年4月15日在8:59

@MattJoiner:之所以没有对FILE *进行类型定义(因为它是一个不透明的指针),是为了允许诸如getc()和putc()之类的宏直接操作FILE结构,并在大多数情况下避免了函数调用的开销。

– chqrlie
16年8月18日在8:37

#2 楼

根据我的经验。隐藏'*'使代码难以阅读。

评论


邓诺对此。我的意思是,如果您有一个指向Device的指针,然后将其typedef定义为Device_Ptr(而不是必须使用Device *)或类似代码,则它非常可读。我看到很多成熟的,相当大的库可以做到这一点。尤其是在C ++中,如果添加模板,则可以省去很多打字的麻烦。

–rbaleksandar
17年2月15日在12:49

@rbaleksandar同样,如果Device *是不透明的句柄也可以。

– Alex
18年4月6日在20:18

作为C n00b,我认为C的指针语法是可憎的-只有那些已经花了10多年学习喜欢它的不幸灵魂所喜欢。每天我都会把myTypePtr或myTypeRef放在那颗星上。 :P

–克里斯托弗·布巴赫(Christoffer Bubach)
6月30日0:37

#3 楼

我唯一在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);


也就是说,SignalFunction是指向带有两个参数(intSigCatcher)并返回SigCatcher的函数的指针。而signal()本身就是一个接受两个参数(intSigCatcher)并返回SigCatcher的函数。


评论


这种类型定义的标点较少吗? “ typedef void SigCatcher(int,void(*)(int))(int)”

– sigjuice
09年4月18日在0:13

没有; gcc说“错误:SigCatcher声明为返回函数的函数”。

–乔纳森·莱弗勒(Jonathan Leffler)
09年4月18日在4:44

我只是遇到了更简单的类型定义的signal()声明freebsd.org/cgi/man.cgi?query=signal

– sigjuice
2010年11月6日在20:17

@Sigjuice:即使没有转到BSD页面(直到编辑材料后才开始这样做),我仍然看到答案存在巨大问题,现已解决。 BSD所谓的sig_t与我的SigCatcher相匹配,是的,它极大地简化了signal()的声明。

–乔纳森·莱弗勒(Jonathan Leffler)
2010年11月6日在21:20

凉。带有显式*的等效声明看起来不错,恕我直言。 typedef void SigCatcher(int); extern SigCatcher *信号(int,SigCatcher *);

– sigjuice
2010年11月6日在22:20

#4 楼

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

int* pointer1, pointer2;


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

typedef int* pInt;
pInt pointer1, pointer2;


它们现在都是int *。

评论


如果您将*触摸指标1而不是触摸“ int”,则声明更有意义的是,指标2不是指针,而只是一个int。

–dreamlax
09年4月15日在4:34

我认为C程序员会争辩说,在声明指针变量时使用解引用运算符更像是一种习惯用法。为了进行检查,在K&R中,他们说:“'int * ip'旨在用作助记符;它表示表达式'* ip'是一个int。”

– hbw
09年4月15日在4:38

但是每个人都知道这种语法,无论如何一次最好声明和初始化一个变量……我很少写int * pointer1,* pointer2;因为在下一行中,您有两个具有未定义值的漂亮指针。

–丹尼尔(Daniel Daranas)
09年4月15日在12:16

@Daniel Daranas:int * p1 = NULL,* p2 = NULL;

–马特细木工
2010年6月9日12:09

用更多的不良实践来捍卫不良实践的典型示例。绝对不要在一行上声明多个变量,因为从来没有理由这样做。这只会导致错误。整个“指针接触”辩论都是胡说八道,因为如果您首先编写好的代码,则*的位置无关紧要。

–伦丁
2015年12月3日,7:13

#5 楼

我的答案是明确的“否”。

为什么?

首先,您只需将单个字符*换成另一个单个字符p。那是零增益。仅此一项就不能使您执行此操作,因为做多余的事情毫无意义,这总是不好的。

其次,这就是重要原因,*的含义不好掩藏。如果我将某些东西传递给类似这样的函数

void foo(SomeType bar);

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


我不希望通过将myBar传递给foo()来改变其含义。毕竟,我按值传递,所以foo()只会看到myBar的副本,对吗?当SomeType别名表示某种指针时,不是这样!

这同时适用于C指针和C ++智能指针:如果隐藏它们是用户指针的事实,则会造成混乱,即完全没有必要。因此,请不要为指针加上别名。

(我相信类型定义指针类型的习惯只是一种误导的尝试,试图隐藏程序员作为程序员的几颗星http://wiki.c2 .com /?ThreeStarProgrammer。)

评论


+1。另一个原因是没有类型系统可以防止void * vp = / * ... * /; foo(vp);或foo(0);。 (用于枚举的Typedef和用于永远不会显示为数字的事物的数字typedef具有类似的问题)。

– PSkocik
18-11-10在21:06



#6 楼

这是风格问题。您在Windows头文件中经常看到这种代码。尽管他们倾向于全部使用大写字母,而不是使用小写字母p作为前缀。

我个人避免使用typedef。让用户明确说他们想要Foo *比PFoo更清楚。 Typedef现在最适合使STL可读:)

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;


#7 楼

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

在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 ++中,这不再是必需的。
主要是因为我们可以通过引用传递东西,并且方法被合并到类声明中。 br />
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.


#8 楼



将其与const混合后,将使您的生活变得悲惨。

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


bar1bar2是否为同一类型?
/>
是的,我只是引用赫伯·萨特的上师。她说了很多实话。 ;)

-编辑-

添加引用文章的链接。

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

评论


您能否为您提到的文章添加链接?此外,他是一个男人! :-)

–法比奥说恢复莫妮卡
19年2月19日在11:13

@Azat Ibrakov这是一个小问题,但是虽然Herb绝对是一个男人,但他写的大师是女性。因此,用女性来称呼她是适当的。是的,我知道她实际上是Herb的另一个自我,但是如果您阅读了整个系列,包括前20篇左右文章中讲述的故事,那么让她成为自己的人就很有意义。

– dgnuff
19/12/31在11:38

#9 楼

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

#10 楼

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

使用指针typedef来表示未标记的结构

问题的大小是

考虑将无标签混凝土(不透明)结构类型定义:

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

/>
成员的详细信息与讨论完全相切;重要的是这不是像typedef这样的不透明类型(并且您不能通过没有标签的typedef struct tag *tag;定义这种不透明类型)。

提出的问题是“如何找到该结构”?

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

实际上,如果要分配这样的指针结构,除非通过动态分配(或模仿动态分配的替代技术),否则您无法创建一个。无法创建结构类型的指针称为sizeof(Information *)的局部变量,也无法创建结构类型的文件范围(全局或Information)变量,也无法嵌入此类结构(而不是指向此类结构的指针)转换为另一种结构或联合类型。

您可以-必须-写:

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


除了将指针隐藏在static中之外,这是一个好习惯-如果typedef的类型发生更改,则大小分配将保持准确。但是在这种情况下,这也是获取结构大小和分配结构的唯一方法。而且没有其他方法可以创建该结构的实例。

这有害吗?

这取决于您的目标。

这不是不透明的类型-当指针类型为info'd时,必须定义结构的详细信息。

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

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

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

但是,总的来说,比启蒙更容易引起混乱和焦虑。

总结

一般来说,使用typedef定义一个指向无标签结构类型的指针。

#11 楼

如果这样做,由于编译器将读取以下内容,将无法创建const pSomeStruct的STL容器:

list<const pSomeStruct> structs;


as

list<SomeStruct * const> structs;


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

请参阅此问题。

评论


当然可以,但是永远不会写list ,因为它无论如何都不会起作用,因此不会造成混乱。

–user267885
2014年4月21日14:32



我不确定“普通类型”是什么意思。 list 是完全有效的,因为const SomeStruct *是CopyAssignable。

–丹胡克
2014年4月22日在17:08



#12 楼

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

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


很不错,但是我认为它并没有增加任何优雅。

评论


这是因为Win32是C API,在C语言中很常见,而不是C ++。

–马丁·约克
09年4月15日在4:36

这个问题同时被标记为C和C ++,您的意思到底是什么?

–dreamlax
09年4月15日在7:38

应该注意的是,这也不是C语言中的常见做法,Win API几乎是唯一一个执行此操作的著名C库。它还使用匈牙利表示法。这是一个非常晦涩的图书馆风格,不是您应该提的经典。

–伦丁
16年4月14日在14:22

这种表示法可以追溯到30年前Windows的起源,该Windows是Intel 16位分段架构。 LP表示长指针,缩写表示LPPOINT ptr;仅此而已,POINT FAR * ptr;的缩写,FAR扩展到很远,后来扩展到__far。所有这一切都相当微不足道,但过去更糟。

– chqrlie
16年8月18日在8:51

#13 楼

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


如果要隐藏实现细节(通常是一件好事),请不要隐藏指针部分。以标准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);


只会起作用,因为您永远不会搞乱直接指向的内容。根据我的经验,在您键入一些指针并在某些地方修改代码的那一刻变得非常难以理解。

#14 楼

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

所以现在我要说:只要明确指出它是“指针”,就可以使用typedef指针。类型”。这意味着您必须专门为其使用前缀/后缀。不,例如,“ p”不是足够的前缀。我可能会选择“ ptr”。

评论


除非您不打算将其用作指针,否则可以将其命名为您的名字。如果仅用于传递内容(例如C FILE *),那么它到底有什么关系呢?

– David Thornley
09年4月15日在21:18

好点子。我试图在反对使用typedef指针的缺点列表中增加一些优点...

–贝诺
09年4月16日在5:48