# const char* vs char const*

Discussion in 'C++' started by engineer, May 28, 2013.

1. ### engineerGuest

Hi,

Can someone give me an easy explanation as well as the difference between the following three statements.

const char* p
char const* m
char* const n

thanks
-Ahmed

engineer, May 28, 2013

2. ### Öö TiibGuest

On Tuesday, 28 May 2013 12:30:39 UTC+3, engineer wrote:
> Can someone give me an easy explanation as well as the difference between
> the following three statements.
>
> const char* p
> char const* m
> char* const n

There are no difference between type of 'p' and 'm'. Both are mutable
pointers to immutable char. 'n' is immutable pointer to mutable char.

Öö Tiib, May 28, 2013

3. ### Gerhard FiedlerGuest

Öö Tiib wrote:

> On Tuesday, 28 May 2013 12:30:39 UTC+3, engineer wrote:
>> Can someone give me an easy explanation as well as the difference between
>> the following three statements.
>>
>> const char* p
>> char const* m
>> char* const n

>
> There are no difference between type of 'p' and 'm'. Both are mutable
> pointers to immutable char. 'n' is immutable pointer to mutable char.

In addition to what Öö wrote: Try to read the type declaration from
right to left, "translating" * with "pointer to", "const" with
"immutable" and considering that an "immutable char" is the same (in
this context) as a "char immutable".

Gerhard

Gerhard Fiedler, May 28, 2013
4. ### James KanzeGuest

On Tuesday, May 28, 2013 10:30:39 AM UTC+1, engineer wrote:
> Can someone give me an easy explanation as well as the
> difference between the following three statements.

> const char* p
> char const* m
> char* const n

Just apply the general rule: const applies to whatever is
immediately to the left of it. If nothing is to the immediate
left of it, move it to the right until there is something.
Thus:

const char* p; // The same as char const* p (which would be preferable)
char const* m; // (non-const) pointer to const char.
char *const n; // Const pointer to (non-const) char.
char const* const* o; // Const pointer to const char.

In an ideal world, you'd never see the first declaration you
give, as it leads to a great deal of confusion when typedefs are
involved:

typedef char* CPtr;
CPtr const p1; // char *const p1
const CPtr p2; // char *const p2 !!!

--
James

James Kanze, May 29, 2013
5. ### Jorgen GrahnGuest

On Wed, 2013-05-29, James Kanze wrote:
> On Tuesday, May 28, 2013 10:30:39 AM UTC+1, engineer wrote:
>> Can someone give me an easy explanation as well as the
>> difference between the following three statements.

>
>> const char* p
>> char const* m
>> char* const n

>
> Just apply the general rule: const applies to whatever is
> immediately to the left of it. If nothing is to the immediate
> left of it, move it to the right until there is something.
> Thus:
>
> const char* p; // The same as char const* p (which would be preferable)
> char const* m; // (non-const) pointer to const char.
> char *const n; // Const pointer to (non-const) char.
> char const* const* o; // Const pointer to const char.
>
> In an ideal world, you'd never see the first declaration you
> give, as it leads to a great deal of confusion when typedefs are
> involved:
>
> typedef char* CPtr;
> CPtr const p1; // char *const p1
> const CPtr p2; // char *const p2 !!!

I think you're wrong here -- the const cannot "invade" the typedef, so
both p1 and p2 are constant pointers to non-const stuff, like you'd
expect.

Of course, many would say that in ideal world you don't see a lot of
typedef:ed pointers either. They are a source of confusion too.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Jorgen Grahn, May 31, 2013
6. ### Öö TiibGuest

On Friday, 31 May 2013 11:33:18 UTC+3, Jorgen Grahn wrote:
> Of course, many would say that in ideal world you don't see a lot of
> typedef:ed pointers either. They are a source of confusion too.

Can you describe the situation where those many would say that?

For example: All C++ standard library containers (and some non-containers)
have 'pointer' and 'const_pointer' member typedefs. Some non-containers
have such typedefs as well. Some people ignore these, some use.
Lack of such typedefs (in a weakly made custom container) may cause
inconvenience for people who expect such typedefs ... but what confusion?

Öö Tiib, May 31, 2013
7. ### James KanzeGuest

On Friday, May 31, 2013 9:33:18 AM UTC+1, Jorgen Grahn wrote:
> On Wed, 2013-05-29, James Kanze wrote:
> > On Tuesday, May 28, 2013 10:30:39 AM UTC+1, engineer wrote:
> >> Can someone give me an easy explanation as well as the
> >> difference between the following three statements.

> >> const char* p
> >> char const* m
> >> char* const n

> > Just apply the general rule: const applies to whatever is
> > immediately to the left of it. If nothing is to the immediate
> > left of it, move it to the right until there is something.
> > Thus:

> > const char* p; // The same as char const* p (which would be preferable)
> > char const* m; // (non-const) pointer to const char.
> > char *const n; // Const pointer to (non-const) char.
> > char const* const* o; // Const pointer to const char.

> > In an ideal world, you'd never see the first declaration you
> > give, as it leads to a great deal of confusion when typedefs are
> > involved:

> > typedef char* CPtr;
> > CPtr const p1; // char *const p1
> > const CPtr p2; // char *const p2 !!!

> I think you're wrong here -- the const cannot "invade" the typedef, so
> both p1 and p2 are constant pointers to non-const stuff, like you'd
> expect.

That's what I said, no?

> Of course, many would say that in ideal world you don't see a lot of
> typedef:ed pointers either. They are a source of confusion too.

I'm tempted to say "wishful thinking". I agree that typedef'ing
pointers is bad policy. But the same problems occur in more
complicated cases, where people are typedef'ing pointers to
member functions, or whatever. (Of course, I would argue that
even then, typedef's cause confusion. But given the syntax,
people do want to do it.)

--
James

James Kanze, May 31, 2013
8. ### James KanzeGuest

On Friday, May 31, 2013 10:05:28 AM UTC+1, Öö Tiib wrote:
> On Friday, 31 May 2013 11:33:18 UTC+3, Jorgen Grahn wrote:
> > Of course, many would say that in ideal world you don't see a lot of
> > typedef:ed pointers either. They are a source of confusion too.

> Can you describe the situation where those many would say that?

> For example: All C++ standard library containers (and some non-containers)
> have 'pointer' and 'const_pointer' member typedefs. Some non-containers
> have such typedefs as well. Some people ignore these, some use.
> Lack of such typedefs (in a weakly made custom container) may cause
> inconvenience for people who expect such typedefs ... but what confusion?

The situation is a bit different there. 'pointer' and
'const_pointer' are *not* necessarily T* and T const*. And the
typedef's are really only for use in template functions; you'd
never use them in cases where you knew the real type.

--
James

James Kanze, May 31, 2013
9. ### Öö TiibGuest

On Friday, 31 May 2013 17:19:07 UTC+3, James Kanze wrote:
> On Friday, May 31, 2013 10:05:28 AM UTC+1, Öö Tiib wrote:
> > On Friday, 31 May 2013 11:33:18 UTC+3, Jorgen Grahn wrote:
> > > Of course, many would say that in ideal world you don't see a lot of
> > > typedef:ed pointers either. They are a source of confusion too.

>
> > Can you describe the situation where those many would say that?

>
> > For example: All C++ standard library containers (and some non-containers)
> > have 'pointer' and 'const_pointer' member typedefs. Some non-containers
> > have such typedefs as well. Some people ignore these, some use.
> > Lack of such typedefs (in a weakly made custom container) may cause
> > inconvenience for people who expect such typedefs ... but what confusion?

>
> The situation is a bit different there. 'pointer' and
> 'const_pointer' are *not* necessarily T* and T const*.

It is guaranteed to be something that works like pointer. It on most cases
is actually pointer (besides vector<bool> and when using some fancy
allocator).

> And the typedef's are really only for use in template functions;
> you'd never use them in cases where you knew the real type.

It does not always matter that you know the real type. For example
'Names' are typedefed 'std::vector<Name>'. Then it happens that you
need a pointer 'n' to element of 'Names' for whatever reason. The
purpose is cleaner (and maintenance easier) if you declare it as
'Names:ointer' instead of 'Name*' despite you know that these
are very same type.

Öö Tiib, May 31, 2013
10. ### James KanzeGuest

On Friday, May 31, 2013 4:41:29 PM UTC+1, Öö Tiib wrote:
> On Friday, 31 May 2013 17:19:07 UTC+3, James Kanze wrote:
> > On Friday, May 31, 2013 10:05:28 AM UTC+1, Öö Tiib wrote:
> > > On Friday, 31 May 2013 11:33:18 UTC+3, Jorgen Grahn wrote:
> > > > Of course, many would say that in ideal world you don't see a lot of
> > > > typedef:ed pointers either. They are a source of confusion too.

> > > Can you describe the situation where those many would say that?

> > > For example: All C++ standard library containers (and some non-containers)
> > > have 'pointer' and 'const_pointer' member typedefs. Some non-containers
> > > have such typedefs as well. Some people ignore these, some use.
> > > Lack of such typedefs (in a weakly made custom container) may cause
> > > inconvenience for people who expect such typedefs ... but what confusion?

> > The situation is a bit different there. 'pointer' and
> > 'const_pointer' are *not* necessarily T* and T const*.

> It is guaranteed to be something that works like pointer. It on most cases
> is actually pointer (besides vector<bool> and when using some fancy
> allocator).

The whole point of having the pointer typedef is that it may not
be a pointer. Or it may be some special type of pointer: all of
these typedef's were introduced to support things like __huge
(or whatever it was) on architectures where pointers could be
declared to have different sizes. Without allocators, the
typedef wouldn't be there.

> > And the typedef's are really only for use in template functions;
> > you'd never use them in cases where you knew the real type.

> It does not always matter that you know the real type. For example
> 'Names' are typedefed 'std::vector<Name>'. Then it happens that you
> need a pointer 'n' to element of 'Names' for whatever reason. The
> purpose is cleaner (and maintenance easier) if you declare it as
> 'Names:ointer' instead of 'Name*' despite you know that these
> are very same type.

The code is considerably more readable if you use Name*, rather
than Names:ointer (and if you use std::vector<Name>, rather
than Names). Maintainable is a point of view: using Names
and Names:ointer has the advantage of not requiring any other
changes (hopefully) if you do later change to use a custom
allocator. Otherwise, it's just obfuscation.

--
James

James Kanze, Jun 2, 2013
11. ### Öö TiibGuest

On Sunday, 2 June 2013 16:40:35 UTC+3, James Kanze wrote:
> On Friday, May 31, 2013 4:41:29 PM UTC+1, Öö Tiib wrote:
> > It does not always matter that you know the real type. For example
> > 'Names' are typedefed 'std::vector<Name>'. Then it happens that you
> > need a pointer 'n' to element of 'Names' for whatever reason. The
> > purpose is cleaner (and maintenance easier) if you declare it as
> > 'Names:ointer' instead of 'Name*' despite you know that these
> > are very same type.

>
> The code is considerably more readable if you use Name*, rather
> than Names:ointer (and if you use std::vector<Name>, rather
> than Names). Maintainable is a point of view: using Names
> and Names:ointer has the advantage of not requiring any other
> changes (hopefully) if you do later change to use a custom
> allocator. Otherwise, it's just obfuscation.

The bloat that results when using some relatively well performing
container without typedefs is too lot for my simple mind. Consider:

boost::intrusive::multiset< Thing, boost::intrusive::member_hook<Thing, boost::intrusive::set_member_hook<>, &Thing::member_hook_> > things;

For me it is hard like that to use that type and to read code that
uses that type. IOW we better remain on different opinions here.
capabilities.

Öö Tiib, Jun 2, 2013
12. ### Jorgen GrahnGuest

On Fri, 2013-05-31, James Kanze wrote:
> On Friday, May 31, 2013 9:33:18 AM UTC+1, Jorgen Grahn wrote:
>> On Wed, 2013-05-29, James Kanze wrote:
>> > On Tuesday, May 28, 2013 10:30:39 AM UTC+1, engineer wrote:
>> >> Can someone give me an easy explanation as well as the
>> >> difference between the following three statements.

>
>> >> const char* p
>> >> char const* m
>> >> char* const n

>
>> > Just apply the general rule: const applies to whatever is
>> > immediately to the left of it. If nothing is to the immediate
>> > left of it, move it to the right until there is something.
>> > Thus:

>
>> > const char* p; // The same as char const* p (which would be preferable)
>> > char const* m; // (non-const) pointer to const char.
>> > char *const n; // Const pointer to (non-const) char.
>> > char const* const* o; // Const pointer to const char.

>
>> > In an ideal world, you'd never see the first declaration you
>> > give, as it leads to a great deal of confusion when typedefs are
>> > involved:

>
>> > typedef char* CPtr;
>> > CPtr const p1; // char *const p1
>> > const CPtr p2; // char *const p2 !!!

>
>> I think you're wrong here -- the const cannot "invade" the typedef, so
>> both p1 and p2 are constant pointers to non-const stuff, like you'd
>> expect.

>
> That's what I said, no?

Oops, sorry! Yes, you did. I saw a claim that wasn't there, namely
that 'const CPtr' and 'CPtr const' would be different things.

I think my misunderstanding was 70% sloppy reading and 30% not being
prepared for the claim that the actual behavior is confusing.
(Although it would be if you mentally expand CPtr to "char *".)

>> Of course, many would say that in ideal world you don't see a lot of
>> typedef:ed pointers either. They are a source of confusion too.

>
> I'm tempted to say "wishful thinking".

Yes, that's what "ideal world" means. But they are actually quite rare
in the code I work with. There may be other ecosystems where they are
common though.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Jorgen Grahn, Jun 3, 2013
13. ### Jorgen GrahnGuest

On Mon, 2013-06-03, Scott Lurndal wrote:
> Leigh Johnston <> writes:
>>On 31/05/2013 15:16, James Kanze wrote:
>>> On Friday, May 31, 2013 9:33:18 AM UTC+1, Jorgen Grahn wrote:
>>>> Of course, many would say that in ideal world you don't see a lot of
>>>> typedef:ed pointers either. They are a source of confusion too.
>>>
>>> I'm tempted to say "wishful thinking". I agree that typedef'ing
>>> pointers is bad policy. But the same problems occur in more
>>> complicated cases, where people are typedef'ing pointers to
>>> member functions, or whatever. (Of course, I would argue that
>>> even then, typedef's cause confusion. But given the syntax,
>>> people do want to do it.)

>>
>>Wrong. typedef is great, use typedef as much as possible! As far as
>>pointers are concerned: use pointers as little as possible!

Disagree ...

If you're thinking of 'typedef unsigned TcpPort;' and such, I prefer
to create small wrapper classes. The typedef gives you a meaningful
name; the class also gives you type safety ('port = date + 1' won't
compile unless you tell it to).

> The _only_ time typedef should be used in C++ code is when a C-type
> function pointer "type" is required.

Disagree ...

> They should never be used with
> structs (e.g. typedef struct {} xx_t, since struct names are equiv to typedef
> in C++. Likewise with enum.
>
> If you're using a pointer, don't hide the declaration in a macro or typedef;
> why make the maintenance programmer go lookup what the typedef is?

Agreed. But these are examples of typedef in C-like scenarios only.
Typedef is important in classes and templates.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Jorgen Grahn, Jun 4, 2013