94

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?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Unknown
  • 45,913
  • 27
  • 138
  • 182
  • 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. – Martin York Apr 15 '09 at 04:37
  • 4
    An interesting article on this general topic: [Conversations: A Midsummer's Night Madness](http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835) See also [Linux Kernel Coding Style](https://www.kernel.org/doc/html/v4.11/process/coding-style.html) for an extreme view of 'do not typedef pointers (and do not provide typedefs for structures or unions)'. – Jonathan Leffler Jan 15 '19 at 05:49
  • @MartinYork One might add that it's not needed in C++, since it has support for pass by reference – klutt May 11 '21 at 09:16
  • @klutt In C++ you use methods rather than function anyway so the hidden object (this) is always there. – Martin York May 11 '21 at 16:06
  • [Apps Hungarian vs Systems Hungarian](https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation). – Gerhard Jul 01 '22 at 07:29

15 Answers15

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.

Artelius
  • 48,337
  • 13
  • 89
  • 105
  • 11
    Real world example: in OS X, the CoreFoundation framework makes extensive use of this technique, referring to them as "opaque" data types. – hbw Apr 15 '09 at 03:32
  • 4
    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. – Adam Rosenfield Apr 15 '09 at 04:42
  • 13
    Counter example: FILE *fp? Actually, I agree with you, but there is ample precedent for the contrary. – Jonathan Leffler Apr 15 '09 at 04:56
  • 12
    'Course, if you do this, you don't name the opaque type "pFoo". – MSalters Apr 15 '09 at 08:59
  • 1
    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. – Jonathan Leffler Apr 16 '09 at 15:47
  • Oh wow, I never considered that `FILE *` was completely unnecessary. It would have been nice if they typedef'd that long ago. – Matt Joiner Jun 09 '10 at 12:07
  • 1
    Note also that `typedef`-ing a pointer can dramatically increase readability. Imagine you have `std::map > >` 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. – rbaleksandar May 30 '16 at 17:07
  • 3
    @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. – chqrlie Aug 18 '16 at 08:37
  • 1
    @rbaleksandar That example is aliasing a type, not the pointer. And the pointer to such a map better shouldn't be aliased. – Aconcagua Dec 11 '18 at 10:17
  • 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... – Aconcagua Dec 11 '18 at 10:21
  • 1
    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). – Aconcagua Dec 11 '18 at 10:24
  • 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?](https://stackoverflow.com/questions/3965279/opaque-c-structs-how-should-they-be-declared/54488289#54488289) – Gabriel Staples Oct 23 '20 at 15:52
82

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

sigjuice
  • 28,661
  • 12
  • 68
  • 93
  • 1
    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 Feb 15 '17 at 12:49
  • @rbaleksandar again it's ok if Device* is an opaque handle. – Alex D Apr 06 '18 at 20:18
  • 1
    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 – Christoffer Bubach Jun 30 '20 at 00:37
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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Does this typedef with less punctuation work? "typedef void SigCatcher(int, void (*)(int))(int)" – sigjuice Apr 18 '09 at 00:13
  • No; gcc says "error: SigCatcher declared as function returning a function". – Jonathan Leffler Apr 18 '09 at 04:44
  • I just came across as even simpler typedef'd declaration of signal() http://www.freebsd.org/cgi/man.cgi?query=signal – sigjuice Nov 06 '10 at 20:17
  • @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. – Jonathan Leffler Nov 06 '10 at 21:20
  • 2
    cool. The equivalent declaration with the explicit * does not look so bad, IMHO. typedef void SigCatcher(int); extern SigCatcher *signal(int, SigCatcher *); – sigjuice Nov 06 '10 at 22:20
18

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.

Sad Developer
  • 1,258
  • 1
  • 12
  • 16
  • 16
    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. – dreamlax Apr 15 '09 at 04:34
  • 3
    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." – hbw Apr 15 '09 at 04:38
  • 2 dreamlax: it is still less readable and consistent code, declaring two variables of different types o one line. – Sad Developer Apr 15 '09 at 06:00
  • 2
    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. – Daniel Daranas Apr 15 '09 at 12:16
  • I think that '''int *pointer, *pointer2''' is still vulnerable to mistakes. – Sad Developer Apr 16 '09 at 02:36
  • 2
    What Roalt meant to say was: 'int \*pointer1' instead of 'int\* pointer1'. – Artelius Nov 08 '09 at 00:52
  • 2
    @Daniel Daranas: `int *p1 = NULL, *p2 = NULL;` – Matt Joiner Jun 09 '10 at 12:09
  • @Matt Joiner You're right. Although I usually use two lines for that anyway. – Daniel Daranas Jun 09 '10 at 12:27
  • 2
    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. – Lundin Dec 03 '15 at 07:13
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 .)

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • +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). – Petr Skocik Nov 10 '18 at 21:06
  • 1
    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 Aug 11 '21 at 15:21
  • @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. – cmaster - reinstate monica Aug 11 '21 at 15:40
  • 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 Aug 11 '21 at 20:01
  • 1
    @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. – cmaster - reinstate monica Aug 11 '21 at 20:18
6

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.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Actually you could get the size without an object by applying `sizeof` on "dereferenced" `NULL` cast to `Information`. Try `sizeof(*(Infomation)0)`. – tstanisl Nov 30 '21 at 22:24
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

dgnuff
  • 3,195
  • 2
  • 18
  • 32
  • 1
    Could you please add a link to the article you mentioned? Besides, he's a man! :-) – Fabio says Reinstate Monica Feb 19 '19 at 11:13
  • 1
    @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. – dgnuff Dec 31 '19 at 11:38
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;
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
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.
Martin York
  • 257,169
  • 86
  • 333
  • 562
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 .

Community
  • 1
  • 1
Dan Hook
  • 6,769
  • 7
  • 35
  • 52
  • Sure, but one would never go about writing `list`, as it wouldn't work anyway, so there is no confusion. –  Apr 21 '14 at 14:32
  • 1
    I'm not sure what you mean by `ordinary_type`. `list` is perfectly valid as `const SomeStruct*` is CopyAssignable. – Dan Hook Apr 22 '14 at 17:08
4

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

Chand
  • 87
  • 4
2

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);
ktb
  • 1,498
  • 10
  • 27
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.

dreamlax
  • 93,976
  • 29
  • 161
  • 209
  • Thta's because the Win32 is a C API and it is common in C not C++ – Martin York Apr 15 '09 at 04:36
  • 2
    The question is tagged both C and C++, what's your point exactly? – dreamlax Apr 15 '09 at 07:38
  • 1
    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. – Lundin Apr 14 '16 at 14:22
  • 1
    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. – chqrlie Aug 18 '16 at 08:51
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.

hlovdal
  • 26,565
  • 10
  • 94
  • 165
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".

Benoît
  • 16,798
  • 8
  • 46
  • 66
  • 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? – David Thornley Apr 15 '09 at 21:18
  • Good point. I was trying to add some pros to the list of cons that had been said against typedef'ing pointers... – Benoît Apr 16 '09 at 05:48