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.
The general rule is that the
const
keyword applies to what precedes it immediately. Exception, a startingconst
applies to what follows.const int*
is the same asint const*
and means "pointer to constant int".const int* const
is the same asint const* const
and means "constant pointer to constant int".Edit: For the Dos and Don'ts, if this answer isn't enough, could you be more precise about what you want?
Read it backwards (as driven by Clockwise/Spiral Rule):
int*
- pointer to intint const *
- pointer to const intint * const
- const pointer to intint const * const
- const pointer to const intNow 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 intint ** const
- a const pointer to a pointer to an intint * const *
- a pointer to a const pointer to an intint const **
- a pointer to a pointer to a const intint * const * const
- a const pointer to a const pointer to an intAnd to make sure we are clear on the meaning of const
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 aconst 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 aT* const
pointer unless you need to allowNULL
pointers.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:
There are many other subtle points surrounding const correctness in C++. I suppose the question here has simply been about C, but I'll give some related examples since the tag is C++ :
You often pass large arguments like strings as
TYPE const &
which prevents the object from being either modified or copied. Example :TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }
But
TYPE & const
is meaningless because references are always const.You should always label class methods that do not modify the class as
const
, otherwise you cannot call the method from aTYPE const &
reference. Example :bool TYPE::operator==(const TYPE &rhs) const { ... }
There are common situations where both the return value and the method should be const. Example :
const TYPE TYPE::operator+(const TYPE &rhs) const { ... }
In fact, const methods must not return internal class data as a reference-to-non-const.
As a result, one must often create both a const and a non-const method using const overloading. For example, if you define
T const& operator[] (unsigned i) const;
, then you'll probably also want the non-const version given by :inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }
Afaik, there are no const functions in C, non-member functions cannot themselves be const in C++, const methods might have side effects, and the compiler cannot use const functions to avoid duplicate function calls. In fact, even a simple
int const &
reference might witness the value to which it refers be changed elsewhere.I think everything is answered here already, but I just want to add that you should beware of
typedef
s! They're NOT just text replacements.For example:
The type of
astring
ischar * const
, notconst char *
. This is one reason I always tend to putconst
to the right of the type, and never at the start.It's simple but tricky. Please note that we can swap the
const
qualifier with any data type (int
,char
,float
, etc.).Let's see the below examples.
const int *p
==>*p
is read-only [p
is a pointer to a constant integer]int const *p
==>*p
is read-only [p
is a pointer to a constant integer]int *p const
==> Wrong Statement. Compiler throws a syntax error.int *const p
==>p
is read-only [p
is a constant pointer to an integer]. As pointerp
here is read-only, the declaration and definition should be in same place.const int *p const
==> Wrong Statement. Compiler throws a syntax error.const int const *p
==>*p
is read-onlyconst int *const p1
==>*p
andp
are read-only [p
is a constant pointer to a constant integer]. As pointerp
here is read-only, the declaration and definition should be in same place.int const *p const
==> Wrong Statement. Compiler throws a syntax error.int const int *p
==> Wrong Statement. Compiler throws a syntax error.int const const *p
==>*p
is read-only and is equivalent toint const *p
int const *const p
==>*p
andp
are read-only [p
is a constant pointer to a constant integer]. As pointerp
here is read-only, the declaration and definition should be in same place.