Is it a good idea to typedef pointers?
Asked Answered
U

15

96

I looked through some code and noticed that the convention was to turn pointer types like

SomeStruct* 

into

typedef SomeStruct* pSomeStruct;

Is there any merit to this?

Urge answered 15/4, 2009 at 3:14 Comment(5)
Its common in C. Because you are trying to hide the fact that it is a pointer. This is the object you pass to all the interfaces in a library. In C++ it is not common and discouraged though not unheard of.Federica
An interesting article on this general topic: Conversations: A Midsummer's Night Madness See also Linux Kernel Coding Style for an extreme view of 'do not typedef pointers (and do not provide typedefs for structures or unions)'.Misinform
@MartinYork One might add that it's not needed in C++, since it has support for pass by referenceGilreath
@Gilreath In C++ you use methods rather than function anyway so the hidden object (this) is always there.Federica
Apps Hungarian vs Systems Hungarian.Illume
B
121

This can be appropriate when the pointer itself can be regarded as a "black box", that is, a piece of data whose internal representation should be irrelevant to the code.

Essentially, if your code will never dereference the pointer, and you just pass it around API functions (sometimes by reference), then not only does the typedef reduce the number of *s in your code, but also suggests to the programmer that the pointer shouldn't really be meddled with.

This also makes it easier to change the API in the future if the need arises. For instance, if you change to using an ID rather than a pointer (or vice versa) existing code won't break because the pointer was never supposed to be dereferenced in the first place.

Buchmanism answered 15/4, 2009 at 3:28 Comment(12)
Real world example: in OS X, the CoreFoundation framework makes extensive use of this technique, referring to them as "opaque" data types.Kolnos
Also in OS X: pthread_t is an opaque type; it is typedef'ed to be a pointer to a 'struct _opaque_pthread_t', which is itself an opaque array of bytes.Wretched
Counter example: FILE *fp? Actually, I agree with you, but there is ample precedent for the contrary.Misinform
'Course, if you do this, you don't name the opaque type "pFoo".Snippet
And the key point is that when you declare a variable of this opaque type, then (outside the library that implements support for the type) you never apply variable->member; you only ever refer to variable and pass it to functions, etc. You don't increment or decrement the variable either.Misinform
Oh wow, I never considered that FILE * was completely unnecessary. It would have been nice if they typedef'd that long ago.Protuberance
Note also that typedef-ing a pointer can dramatically increase readability. Imagine you have std::map<int, std::map<std::string, std::vector<int> > > as a variable. If you have to use define a lot of such variables in different places of your code using a nice typedef can make working and also reading the code a more pleasant experience.Pistachio
@MattJoiner: the reason FILE* was not typedef'd as an opaque pointer is to allow macros such as getc() and putc() to manipulate the FILE structure directly and avoid the function call overhead in most cases.Semela
@Pistachio That example is aliasing a type, not the pointer. And the pointer to such a map better shouldn't be aliased.Tabulator
In addition to @JonathanLeffler: Best if - in the public header - the type for the opaque pointer was only declared, but defined elsewhere: struct XYZ; typedef struct XYZ* Handle; - if the implementation is needed accross several compilation units, it might be defined in a separate, private header...Tabulator
Totally agree on the given use case, would appreciate, though, if answer was a bit more explicit about that this should be the exceptional case (wheras, in general, if the pointer is intended to be dereferenced, it should better not be aliased).Tabulator
Some people might want to see an example of a "black box" pointer which shouldn't be directly meddled with. So, here's a full example I wrote of an "opaque pointer" (or: pointer to an opaque struct) in C which demonstrates this perfectly, including the typedef of the opaque pointer. Opaque C structs: how should they be declared?Scheffler
F
83

Not in my experience. Hiding the '*' makes the code hard to read.

Folliculin answered 15/4, 2009 at 3:17 Comment(3)
Dunno about that. I mean if you have a pointer to let's say Device and you typedef it to Device_Ptr (instead of having to use Device *) or similar it's quite readable. I see a lot of well matured and pretty big libraries that do that. Especially in C++ if you add templates this can spare your fingers quite the typing.Pistachio
@Pistachio again it's ok if Device* is an opaque handle.Shocking
As a C n00b I think C's pointer syntax is an abomination - only liked by those unfortunate souls that already spent 10+ years learning to like it. I'd take myTypePtr or myTypeRef over that star any day. :PMaccabean
M
29

The only time I use a pointer inside the typedef is when dealing with pointers to functions:

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

typedef void (*SigCatcher)(int);

SigCatcher old = signal(SIGINT, SIG_IGN);

Otherwise, I find them more confusing than helpful.


The struck-out declaration is the correct type for a pointer to the signal() function, not of the signal catcher. It could be made clearer (using the corrected SigCatcher type above) by writing:
 typedef SigCatcher (*SignalFunction)(int, SigCatcher);

Or, to declare the signal() function:

 extern SigCatcher signal(int, SigCatcher);

That is, a SignalFunction is a pointer to a function which takes two arguments (an int and a SigCatcher) and returns a SigCatcher. And signal() itself is a function which takes two arguments (an int and a SigCatcher) and returns a SigCatcher.

Misinform answered 15/4, 2009 at 3:34 Comment(5)
Does this typedef with less punctuation work? "typedef void SigCatcher(int, void (*)(int))(int)"Folliculin
No; gcc says "error: SigCatcher declared as function returning a function".Misinform
I just came across as even simpler typedef'd declaration of signal() freebsd.org/cgi/man.cgi?query=signalFolliculin
@Sigjuice: even without going to the BSD page (which I didn't do until after I'd edited the material), I saw huge problems with my answer, which are now fixed. And what BSD calls sig_t matches my SigCatcher, and yes, it simplifies the declaration of signal() enormously.Misinform
cool. The equivalent declaration with the explicit * does not look so bad, IMHO. typedef void SigCatcher(int); extern SigCatcher *signal(int, SigCatcher *);Folliculin
L
19

This can help you avoid some errors. For example in following code:

int* pointer1, pointer2;

pointer2 is not an int *, it is simple int. But with typedefs this is not gonna happen:

typedef int* pInt;
pInt pointer1, pointer2;

They are both int * now.

Laity answered 15/4, 2009 at 3:52 Comment(9)
If you put the * touching pointer1 rather than touching "int", the declaration makes much more sense that pointer2 is not a pointer, just an int.Rentfree
I think C programmers would argue that using the dereferencing operator when declaring a pointer variable is more of an idiomatic thing. For examine, in K&R, they say: "'int *ip' intended as a mnemonic; it says that the expression '*ip' is an int."Kolnos
2 dreamlax: it is still less readable and consistent code, declaring two variables of different types o one line.Laity
But everyone knows this syntax, and it's best to declare and initialize one variable at a time anyway... I seldom if ever write int *pointer1, *pointer2; because in the next line you have two nice pointers with undefined value.Wane
I think that '''int *pointer, *pointer2''' is still vulnerable to mistakes.Laity
What Roalt meant to say was: 'int *pointer1' instead of 'int* pointer1'.Buchmanism
@Daniel Daranas: int *p1 = NULL, *p2 = NULL;Protuberance
@Matt Joiner You're right. Although I usually use two lines for that anyway.Wane
Typical example of defending bad practice with even more bad practice. Never declare more than one variable on a single line, because there is never a reason to do so, period. It will only lead to bugs. And the whole "pointer touching" debate is nonsense, because if you write good code in the first place, the location of the * doesn't matter.Mulvey
E
16

My answer is a clear "No".

Why?

Well, first of all, you simply exchange a single character * for another single character p. That is zero gain. This alone should keep you from doing this as it is always bad to do extra stuff that's pointless.

Second, and that is the important reason, the * carries meaning that is not good to hide. If I pass something to a function like this

void foo(SomeType bar);

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

I do not expect the meaning of myBar to be changed by passing it to foo(). After all, I'm passing by value, so foo() only ever sees a copy of myBar right? Not when SomeType is aliased to mean some kind of pointer! Especially when that pointer acts as a reference to some kind of object whose value is basically the meaning of the pointer: I won't care that the pointer itself does not change (due to pass-by-value), I'm interested in whether the object changes or not (the object behind the pointer).

This applies both to C pointers and C++ smart pointers: If you hide the fact that they are pointers to your users, you will create confusion that is totally unnecessary. So, please, don't alias your pointers.

(I believe the habit of typedefing pointer types is just a misguided attempt to hide how many stars one has as a programmer http://wiki.c2.com/?ThreeStarProgrammer .)

Everglades answered 9/4, 2017 at 21:40 Comment(5)
+1. Another reason is there will be no typesystem protection from void *vp=/*...*/; foo(vp); or foo(0);. (Typedefs for enums and numerical typedefs for things that should never ever show up as numbers have similar problems).Crosstie
Even if you pass the pointer to a function, the pointer myBar itself is still being copied, even if the data that it points to is not. So the value of myBar (which is an address) still cannot be changed by foo, but the value pointed to by myBar can.Celestina
@Celestina Yes. So? - My point is that a pointer usually serves as a proxy for some object in memory. The semantics of the pointer are pretty much the semantics of the object behind it, and those can be changed by any function receiving the pointer. For instance, if you had a type struct _fraction and typedefed its pointer to Fraction, the statements Fraction a = fraction(84, 2); printFrac(a); doSomething(a); printFrac(a); could yield confusing output. With Fraction* a = fraction(84, 2); it is clear that doSomething(a) may change the meaning of a by changing the object.Everglades
Yes, you are right. And I agree that typedef-ing pointers is generally an obfuscation. I just don't want anyone reading this to conflate the meaning/semantics of a pointer and its value, which are separate and distinct. Remembering that difference is critical when reassigning pointer variables, or using pointers-to-pointers.Celestina
@Celestina Ah, now I see. Well, I've added some words at the point in question in the hope of making my meaning clearer. Thanks for pointing that out.Everglades
M
8

Discussion pitched assuming the language of interest is C. Ramifications for C++ have not been considered.

Using a a pointer typedef for an untagged structure

The question Size of a struct that is defined as a pointer raises an interesting side-light on using typedef for (structure) pointers.

Consider the tagless concrete (not opaque) structure type definition:

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

The details of the members are completely tangential to this discussion; all that matters is that this not an opaque type like typedef struct tag *tag; (and you can't define such opaque types via a typedef without a tag).

The question raised is 'how can you find the size of that structure'?

The short answer is 'only via a variable of the type'. There is no tag to use with sizeof(struct tag). You can't usefully write sizeof(*Information), for example, and sizeof(Information *) is the size of a pointer to the pointer type, not the size of the structure type.

In fact, if you want to allocate such a structure, you can't create one except via dynamic allocation (or surrogate techniques that mimic dynamic allocation). There is no way to create a local variable of the structure type whose pointers are called Information, nor is there a way to create a file scope (global or static) variable of the structure type, nor is there a way to embed such a structure (as opposed to a pointer to such a structure) into another structure or union type.

You can — must — write:

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

Apart from the fact that the pointer is hidden in the typedef, this is good practice — if the type of info changes, the size allocation will remain accurate. But in this case, it is also the only way to get the size of the structure and to allocate the structure. And there's no other way to create an instance of the structure.

Is this harmful?

It depends on your goals.

This isn't an opaque type — the details of the structure must be defined when the pointer type is typedef'd.

It is a type that can only be used with dynamic memory allocation.

It is a type that is nameless. The pointer to the structure type has a name, but the structure type itself does not.

If you want to enforce dynamic allocation, this seems to be a way to do it.

On the whole, though, it is more likely to cause confusion and angst than enlightenment.

Summary

It is, in general, a bad idea to use typedef to define a pointer to a tagless stucture type.

Misinform answered 9/4, 2017 at 19:52 Comment(1)
Actually you could get the size without an object by applying sizeof on "dereferenced" NULL cast to Information. Try sizeof(*(Infomation)0).Abusive
B
5

This is a matter of style. You see this kind of code very frequently in the Windows header files. Though they tend to prefer the all upper case version instead of prefixing with a lower case p.

Personally I avoid this use of typedef. It's much clearer to have the user explicitly say they want a Foo* than PFoo. Typedef's are best suited these days for making STL readable :)

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
Banksia answered 15/4, 2009 at 3:19 Comment(0)
F
5

It (like so many answers) depends.

In C this is very common as you are trying to disguise that an object is a pointer. You are trying to imply that this is the object that all your functions manipulate (we know it is a pointer underneath but it represents the object you are manipulating).

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

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

Underneath MYDB is probably a pointer at some object.

In C++ this is no longer required.
Mainly because we can pass things around by reference and the methods are incorporated into the class declaration.

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.
Federica answered 15/4, 2009 at 4:45 Comment(0)
H
5

No.

It will make your life miserable the moment you mix it with const

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

Are bar1 and bar2 the same type?

And yeah, I am just quoting Herb Sutter's Guru. Much truth did she speak. ;)

-- Edit --

Adding link to cited article.

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

Hilariohilarious answered 16/1, 2019 at 8:57 Comment(2)
Could you please add a link to the article you mentioned? Besides, he's a man! :-)Federalism
@Azat Ibrakov It's a minor point, but while Herb is definitely a guy, the Guru he writes about is female. Hence it is appropriate to refer to her in the feminine. Yeah, I know she's effectively an alter ego for Herb, but if you read the entire series, including the story told in the first 20 or so articles, it makes sense for her to be who she is.Hilariohilarious
S
4

Typedef is used to make code more readable, but making pointer as typedef will increase confusion. Better to avoid typedef pointers.

Snooty answered 15/4, 2009 at 3:55 Comment(0)
A
4

If you do this, you will be unable to create STL containers of const pSomeStruct since the compiler reads:

list<const pSomeStruct> structs;

as

list<SomeStruct * const> structs;

which is not a legal STL container since the elements are not assignable.

See this question .

Adapa answered 27/1, 2010 at 17:24 Comment(2)
Sure, but one would never go about writing list<const ordinary_type>, as it wouldn't work anyway, so there is no confusion.Committee
I'm not sure what you mean by ordinary_type. list<const SomeStruct*> is perfectly valid as const SomeStruct* is CopyAssignable.Adapa
C
3

In general, no. In specific cases, yes.

There are a couple constructs that some other answers alluded to, and that is pointer-only types. There are a couple pointer-only type constructs that come to mind. If anyone thinks of more I'll add them to the list.

Opaque Types

These are types where the type's implementation is totally hidden to the user. You will typically see a structure typedef in the header, but no implementation of that struct. Because of that you cannot dereference values of these types. All functions that operate on this type take pointers to these types. It is appropriate to add the pointer to the typedef in these situations. You often see these called "handle" types.

typedef struct handle_ * handle;

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

Flexible Array Member Types

Another pointer-only type are types that are defined with flexible array members (FAMs). The last member in a FAM type is an unconstrained array. You are intended to dynamically allocate storage for these types and the flexible array is treated as inline with the structure. You can access fields in a FAM type, but cannot dereference the whole object. It is also appropriate to add the pointer to the typedef here.

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);
Capapie answered 30/11, 2021 at 21:58 Comment(1)
Yes! Dear god! Somebody has a sensible view.Synonymous
R
1

Win32 API does this with just about every structure (if not all)

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

It's nice how it is consistent, but in my opinion it doesn't add any elegance.

Rentfree answered 15/4, 2009 at 3:42 Comment(4)
Thta's because the Win32 is a C API and it is common in C not C++Federica
The question is tagged both C and C++, what's your point exactly?Rentfree
It should be noted that this is not common practice in C either, the Win API is pretty much the only well-known C library that does this. It also uses Hungarian notation. It is a very obscure library style-wise, it is not something you should bring up as canon.Mulvey
This notation goes back to the origin of Windows, 30 years ago, in the Intel 16 bit segmented architecture. LP stands for long pointer and the shorthand notation LPPOINT ptr; was nothing more than that, a handy abbreviation for POINT FAR *ptr;, FAR expanding to far and later to __far. All this is quite inelegant, but it used to be even worse.Semela
C
1

The purpose with typedef is to hide the implementation details, but typedef-ing the pointer property hides too much and makes the code harder to read/understand. So please do not do that.


If you want to hide implementation details (which often is a good thing to do), do not hide the pointer part. Take for instance at the prototype for the standard FILE interface:

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

here fopen returns a pointer to some structure FILE (which you do not know the implementation details for). Maybe FILE is not such a good example because in this case it could have worked with some pFILE type that hid the fact that it is a pointer.

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

However, that would only work because you never mess around with the content that is pointed to directly. The moment you typedef some pointer that you some places modify the code becomes very hard to read in my experience.

Crooked answered 15/4, 2009 at 11:56 Comment(0)
F
0

Some time ago, i'd have answered "no" to this question. Now, with the rise of smart pointers, pointers are not always defined with a star '*' anymore. So there is nothing obvious about a type being a pointer or not.

So now i'd say : it is fine to typedef pointers, as long as it is made very clear that it is a "pointer type". That means you have to use a prefix/suffix specifically for it. No, "p" is not a sufficient prefix, for instance. I'd probably go with "ptr".

Fanya answered 15/4, 2009 at 5:55 Comment(2)
Unless you don't intend it to be used as a pointer, in which case you can name it what you will. If it's just used to pass stuff around (like a C FILE *), does it matter exactly what it is?Timeless
Good point. I was trying to add some pros to the list of cons that had been said against typedef'ing pointers...Thenceforward

© 2022 - 2025 — McMap. All rights reserved.