Categories
c c++ c++-faq constants pointers

What is the difference between const int*, const int * const, and int const *?

1666

I always mess up how to use const int*, const int * const, and int const * correctly. Is there a set of rules defining what you can and cannot do?

I want to know all the do’s and all don’ts in terms of assignments, passing to the functions, etc.

12

  • 225

    You can use the “Clockwise/Spiral Rule” to decipher most C and C++ declarations.

    Jun 13, 2010 at 20:49

  • 66

    cdecl.org is a great website which auto-translates C declarations for you.

    – Dave

    Nov 2, 2010 at 19:37


  • 9

    @Calmarius: start where the type-name is / should be, move right when you can, left when you must. int *(*)(char const * const). Start to the right of the parenthesized * then we have to move left: pointer. Outside the parens, we can move right: pointer to function of .... Then we have to move left: pointer to function of ... that returns pointer to int. Repeat to expand the parameter (the ...): pointer to function of (constant pointer to constant char) that returns pointer to int. What would the equivalent one-line declaration be in a easy-reading language like Pascal?

    Jul 9, 2015 at 17:08


  • 3

    @MarkKCowan In Pascal it would be something like function(x:^char):^int. There function types are imply a pointer to a function so no need to specify it, and Pascal doesn’t enforce const correctness. It can be read from left to right.

    – Calmarius

    Jul 9, 2015 at 20:54


  • 9

    The first thing to the left of the “const” is what’s constant. If “const” is the thing the farthest to the left, then the first thing to the right of it is what’s constant.

    – Cupcake

    Jul 31, 2016 at 4:41

2643

Read it backwards (as driven by Clockwise/Spiral Rule):

  • int* – pointer to int
  • int const * – pointer to const int
  • int * const – const pointer to int
  • int const * const – const pointer to const int

Now the first const can be on either side of the type so:

  • const int * == int const *
  • const int * const == int const * const

If you want to go really crazy you can do things like this:

  • int ** – pointer to pointer to int
  • int ** const – a const pointer to a pointer to an int
  • int * const * – a pointer to a const pointer to an int
  • int const ** – a pointer to a pointer to a const int
  • int * const * const – a const pointer to a const pointer to an int

And to make sure we are clear on the meaning of const:

int a = 5, b = 10, c = 15;

const int* foo;     // pointer to constant int.
foo = &a;           // assignment to where foo points to.

/* dummy statement*/
*foo = 6;           // the value of a can´t get changed through the pointer.

foo = &b;           // the pointer foo can be changed.



int *const bar = &c;  // constant pointer to int 
                      // note, you actually need to set the pointer 
                      // here because you can't change it later ;)

*bar = 16;            // the value of c can be changed through the pointer.    

/* dummy statement*/
bar = &a;             // not possible because bar is a constant pointer.           

foo is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to a const char. You may change which string you point to but you can’t change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn’t be changed.

bar is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use a T* const pointer unless you need to allow NULL pointers.

16

  • 562

    I would like to append a rule of thumb which may help you remember how to discover whether ‘const’ applies to pointer or to pointed data: split the statement at asterix sign, then, if the const keyword appears in the left part (like in ‘const int * foo’) – it belongs to pointed data, if it’s in the right part (‘int * const bar’) – it’s about the pointer.

    – Michael

    Jul 17, 2009 at 17:26

  • 22

    @Michael: Kudos to Michael for such a simple rule for remembering/understanding const rule.

    – sivabudh

    Feb 11, 2010 at 19:00

  • 11

    @Jeffrey: read it backwards works well as long as there are no parenthesis. Then, well… use typedefs

    May 28, 2013 at 19:53


  • 13

    +1, though a better summary would be: read pointer declarations backwards, that means, close to @Michael ‘s statement: stop the normal left-to-right reading at the first asterisk.

    – Wolf

    Jun 18, 2014 at 9:21


  • 4

    @gedamial it does, it works fine, but you must assign it at the same time you declare it (because you can’t reassign a “const pointer”). const int x = 0; const int *const px = &x; const int *const *const p = &px; works just fine.

    – RastaJedi

    Aug 8, 2016 at 23:15

481

For those who don’t know about Clockwise/Spiral Rule:
Start from the name of the variable, move clockwisely (in this case, move backward) to the next pointer or type. Repeat until expression ends.

Here is a demo:

pointer to int

const pointer to int const

pointer to int const

pointer to const int

const pointer to int

12

  • 9

    @Jan the link for the complex example does not have permissions. can you post it directly here, or remove the viewing restrictions?

    – R71

    Apr 8, 2016 at 12:03

  • 9

    @Rog it used to have all open access permissions… I didn’t write the article and don’t have access permissions myself, unfortunately. However, here is an archived version of the article that still works: archive.is/SsfMX

    Apr 8, 2016 at 13:34

  • 11

    The complex example is still just right to left, but includes resolving parentheses the way one would normally. The whole clockwise spiral thing doesn’t make that any easier.

    Sep 18, 2016 at 23:04

  • 12

    Ultimate example: void (*signal(int, void (*fp)(int)))(int); from archive.is/SsfMX

    May 3, 2017 at 19:04

  • 6

    Do not rely on this rule. This is not universal. There are some cases where it fails.

    – haccks

    May 12, 2017 at 7:17

176

I think everything is answered here already, but I just want to add that you should beware of typedefs! They’re NOT just text replacements.

For example:

typedef char *ASTRING;
const ASTRING astring;

The type of astring is char * const, not const char *. This is one reason I always tend to put const to the right of the type, and never at the start.

7

  • 33

    And for me this is the reason to never typedef pointers. I don’t see the benefit in things like typedef int* PINT (I assume its something that came from practices in C and many developers kept doing it). Great, I replaced that * with a P, it doesn’t speed up typing, plus introducing the issue you mention.

    – Mephane

    Jan 28, 2011 at 13:01

  • 2

    @Mephane – I can see that. However, to me it seems kind of backwards to avoid a nice language feature in order to keep using an exceptional syntactical rule (about “const” placement), rather than avoiding using the exceptional syntactic rule so you can safely make use of this language feature.

    – T.E.D.

    Oct 17, 2012 at 14:06


  • 8

    @Mephane PINT is indeed a rather dumb usage of a typedef, especially cuz it makes me think that the system stores uses beer for memory. typedef s are pretty useful for dealing with pointers to functions, though.

    Dec 26, 2013 at 21:07

  • 6

    @KazDragon THANKS! Without it, I would’ve messed up with all those typedefed PVOID, LPTSTR stuff in Win32 api!

    – David Lee

    May 8, 2014 at 12:29


  • 3

    @Mephane: I’ve had to use pSomething a couple of times when using certain legacy macros which were written to accept a type, but would break apart if the type wasn’t a single alphanumeric identifier. 🙂

    – Groo

    May 8, 2017 at 16:37