98

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?

5
  • 1
    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. Commented Apr 15, 2009 at 4:37
  • 4
    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)'. Commented Jan 15, 2019 at 5:49
  • @MartinYork One might add that it's not needed in C++, since it has support for pass by reference Commented May 11, 2021 at 9:16
  • @klutt In C++ you use methods rather than function anyway so the hidden object (this) is always there. Commented May 11, 2021 at 16:06
  • Apps Hungarian vs Systems Hungarian. Commented Jul 1, 2022 at 7:29

15 Answers 15

124

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.

Sign up to request clarification or add additional context in comments.

12 Comments

Real world example: in OS X, the CoreFoundation framework makes extensive use of this technique, referring to them as "opaque" data types.
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.
Counter example: FILE *fp? Actually, I agree with you, but there is ample precedent for the contrary.
'Course, if you do this, you don't name the opaque type "pFoo".
@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.
|
83

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

4 Comments

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.
@rbaleksandar again it's ok if Device* is an opaque handle.
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. :P
If, from the beginning, the standard had been to typedef struct myStruct *mystruct ... it would not be difficult to read. Context is priceless. In my code, if I'm declaring the struct as a value type, I use struct myStruct' ... if I'm using as a reference, I declare with mystruct`.
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.

5 Comments

Does this typedef with less punctuation work? "typedef void SigCatcher(int, void (*)(int))(int)"
No; gcc says "error: SigCatcher declared as function returning a function".
I just came across as even simpler typedef'd declaration of signal() freebsd.org/cgi/man.cgi?query=signal
@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.
cool. The equivalent declaration with the explicit * does not look so bad, IMHO. typedef void SigCatcher(int); extern SigCatcher *signal(int, SigCatcher *);
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.

12 Comments

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.
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."
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.
What Roalt meant to say was: 'int *pointer1' instead of 'int* pointer1'.
@Daniel Daranas: int *p1 = NULL, *p2 = NULL;
|
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 .)

5 Comments

+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).
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.
@Aposhian 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.
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.
@Aposhian 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.
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.

1 Comment

Actually you could get the size without an object by applying sizeof on "dereferenced" NULL cast to Information. Try sizeof(*(Infomation)0).
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. 

Comments

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

2 Comments

Could you please add a link to the article you mentioned? Besides, he's a man! :-)
@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.
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 std::map<std::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap; 

Comments

4

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

Comments

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 .

2 Comments

Sure, but one would never go about writing list<const ordinary_type>, as it wouldn't work anyway, so there is no confusion.
I'm not sure what you mean by ordinary_type. list<const SomeStruct*> is perfectly valid as const SomeStruct* is CopyAssignable.
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); 

1 Comment

Yes! Dear god! Somebody has a sensible view.
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.

4 Comments

Thta's because the Win32 is a C API and it is common in C not C++
The question is tagged both C and C++, what's your point exactly?
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.
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.
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.

Comments

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".

2 Comments

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?
Good point. I was trying to add some pros to the list of cons that had been said against typedef'ing pointers...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.