Related
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.
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.
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:
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.
Like pretty much everyone pointed out:
What’s the difference between const X* p, X* const p and const X* const p?
You have to read pointer declarations
right-to-left.
const X* p means "p points to an X that is const": the X object can't be changed via p.
X* const p means "p is a const pointer to an X that is non-const": you can't change the pointer p itself, but you can change the X object via p.
const X* const p means "p is a const pointer to an X that is const": you can't change the pointer p itself, nor can you change the X object via p.
Constant reference:
A reference to a variable (here int), which is constant. We pass the variable as a reference mainly, because references are smaller in size than the actual value, but there is a side effect and that is because it is like an alias to the actual variable. We may accidentally change the main variable through our full access to the alias, so we make it constant to prevent this side effect.
int var0 = 0;
const int &ptr1 = var0;
ptr1 = 8; // Error
var0 = 6; // OK
Constant pointers
Once a constant pointer points to a variable then it cannot point to any other variable.
int var1 = 1;
int var2 = 0;
int *const ptr2 = &var1;
ptr2 = &var2; // Error
Pointer to constant
A pointer through which one cannot change the value of a variable it points is known as a pointer to constant.
int const * ptr3 = &var2;
*ptr3 = 4; // Error
Constant pointer to a constant
A constant pointer to a constant is a pointer that can neither change the address it's pointing to and nor can it change the value kept at that address.
int var3 = 0;
int var4 = 0;
const int * const ptr4 = &var3;
*ptr4 = 1; // Error
ptr4 = &var4; // Error
This question shows precisely why I like to do things the way I mentioned in my question is const after type id acceptable?
In short, I find the easiest way to remember the rule is that the "const" goes after the thing it applies to. So in your question, "int const *" means that the int is constant, while "int * const" would mean that the pointer is constant.
If someone decides to put it at the very front (eg: "const int *"), as a special exception in that case it applies to the thing after it.
Many people like to use that special exception because they think it looks nicer. I dislike it, because it is an exception, and thus confuses things.
The general rule is that the const keyword applies to what precedes it immediately. Exception, a starting const applies to what follows.
const int* is the same as int const* and means "pointer to constant int".
const int* const is the same as int 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?
Simple Use of const.
The simplest use is to declare a named constant. To do this, one declares a constant as if it was a variable but add const before it. One has to initialize it immediately in the constructor because, of course, one cannot set the value later as that would be altering it. For example:
const int Constant1=96;
will create an integer constant, unimaginatively called Constant1, with the value 96.
Such constants are useful for parameters which are used in the program but are do not need to be changed after the program is compiled. It has an advantage for programmers over the C preprocessor #define command in that it is understood & used by the compiler itself, not just substituted into the program text by the preprocessor before reaching the main compiler, so error messages are much more helpful.
It also works with pointers but one has to be careful where const to determine whether the pointer or what it points to is constant or both. For example:
const int * Constant2
declares that Constant2 is variable pointer to a constant integer and:
int const * Constant2
is an alternative syntax which does the same, whereas
int * const Constant3
declares that Constant3 is constant pointer to a variable integer and
int const * const Constant4
declares that Constant4 is constant pointer to a constant integer. Basically ‘const’ applies to whatever is on its immediate left (other than if there is nothing there in which case it applies to whatever is its immediate right).
ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
It's simple but tricky. Please note that we can apply the const qualifier to 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 pointer p 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-only
const int *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p 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 to int const *p
int const *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p here is read-only, the declaration and definition should be in same place.
I had the same doubt as you until I came across this book by the C++ Guru Scott Meyers. Refer the third Item in this book where he talks in details about using const.
Just follow this advice
If the word const appears to the left of the asterisk, what's pointed to is constant
If the word const appears to the right of the asterisk, the pointer itself is constant
If const appears on both sides, both are constant
To remember in easy way :
If const is before * then value is constant.
If const is after * then address is constant.
if const are available both before and after * then both value and address are constant.
e.g.
int * const var; //here address is constant.
int const * var; //here value is constant.
int const * const var; // both value and address are constant.
The C and C++ declaration syntax has repeatedly been described as a failed experiment, by the original designers.
Instead, let's name the type “pointer to Type”; I’ll call it Ptr_:
template< class Type >
using Ptr_ = Type*;
Now Ptr_<char> is a pointer to char.
Ptr_<const char> is a pointer to const char.
And const Ptr_<const char> is a const pointer to const char.
For me, the position of const i.e. whether it appears to the LEFT or RIGHT or on both LEFT and RIGHT relative to the * helps me figure out the actual meaning.
A const to the LEFT of * indicates that the object pointed by the pointer is a const object.
A const to the RIGHT of * indicates that the pointer is a const pointer.
The following table is taken from Stanford CS106L Standard C++ Programming Laboratory Course Reader.
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 a TYPE 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 drew an image below to explain this, maybe helpful.
int const v and const int v are identical.
The const with the int on either sides will make pointer to constant int:
const int *ptr=&i;
or:
int const *ptr=&i;
const after * will make constant pointer to int:
int *const ptr=&i;
In this case all of these are pointer to constant integer, but none of these are constant pointer:
const int *ptr1=&i, *ptr2=&j;
In this case all are pointer to constant integer and ptr2 is constant pointer to constant integer. But ptr1 is not constant pointer:
int const *ptr1=&i, *const ptr2=&j;
if const is to the left of *, it refers to the value (it doesn't matter whether it's const int or int const)
if const is to the right of *, it refers to the pointer itself
it can be both at the same time
An important point: const int *p does not mean the value you are referring to is constant!!. It means that you can't change it through that pointer (meaning, you can't assign $*p = ...`). The value itself may be changed in other ways. Eg
int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error
This is meant to be used mostly in function signatures, to guarantee that the function can't accidentally change the arguments passed.
This mostly addresses the second line: best practices, assignments, function parameters etc.
General practice. Try to make everything const that you can. Or to put that another way, make everything const to begin with, and then remove exactly the minimum set of consts necessary to allow the program to function. This will be a big help in attaining const-correctness, and will help ensure that subtle bugs don't get introduced when people try and assign into things they're not supposed to modify.
Avoid const_cast<> like the plague. There are one or two legitimate use cases for it, but they are very few and far between. If you're trying to change a const object, you'll do a lot better to find whoever declared it const in the first pace and talk the matter over with them to reach a consensus as to what should happen.
Which leads very neatly into assignments. You can assign into something only if it is non-const. If you want to assign into something that is const, see above. Remember that in the declarations int const *foo; and int * const bar; different things are const - other answers here have covered that issue admirably, so I won't go into it.
Function parameters:
Pass by value: e.g. void func(int param) you don't care one way or the other at the calling site. The argument can be made that there are use cases for declaring the function as void func(int const param) but that has no effect on the caller, only on the function itself, in that whatever value is passed cannot be changed by the function during the call.
Pass by reference: e.g. void func(int ¶m) Now it does make a difference. As just declared func is allowed to change param, and any calling site should be ready to deal with the consequences. Changing the declaration to void func(int const ¶m) changes the contract, and guarantees that func can now not change param, meaning what is passed in is what will come back out. As other have noted this is very useful for cheaply passing a large object that you don't want to change. Passing a reference is a lot cheaper than passing a large object by value.
Pass by pointer: e.g. void func(int *param) and void func(int const *param) These two are pretty much synonymous with their reference counterparts, with the caveat that the called function now needs to check for nullptr unless some other contractual guarantee assures func that it will never receive a nullptr in param.
Opinion piece on that topic. Proving correctness in a case like this is hellishly difficult, it's just too damn easy to make a mistake. So don't take chances, and always check pointer parameters for nullptr. You will save yourself pain and suffering and hard to find bugs in the long term. And as for the cost of the check, it's dirt cheap, and in cases where the static analysis built into the compiler can manage it, the optimizer will elide it anyway. Turn on Link Time Code Generation for MSVC, or WOPR (I think) for GCC, and you'll get it program wide, i.e. even in function calls that cross a source code module boundary.
At the end of the day all of the above makes a very solid case to always prefer references to pointers. They're just safer all round.
Just for the sake of completeness for C following the others explanations, not sure for C++.
pp - pointer to pointer
p - pointer
data - the thing pointed, in examples x
bold - read-only variable
Pointer
p data - int *p;
p data - int const *p;
p data - int * const p;
p data - int const * const p;
Pointer to pointer
pp p data - int **pp;
pp p data - int ** const pp;
pp p data - int * const *pp;
pp p data - int const **pp;
pp p data - int * const * const pp;
pp p data - int const ** const pp;
pp p data - int const * const *pp;
pp p data - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
N-levels of Dereference
Just keep going, but may the humanity excommunicate you.
int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;
printf("%d \n", ****pppp);
const int* - pointer to constant int object.
You can change the value of the pointer; you can not change the value of the int object, the pointer points to.
const int * const - constant pointer to constant int object.
You can not change the value of the pointer nor the value of the int object the pointer points to.
int const * - pointer to constant int object.
This statement is equivalent to 1. const int* - You can change the value of the pointer but you can not change the value of the int object, the pointer points to.
Actually, there is a 4th option:
int * const - constant pointer to int object.
You can change the value of the object the pointer points to but you can not change the value of the pointer itself. The pointer will always point to the same int object but this value of this int object can be changed.
If you want to determine a certain type of C or C++ construct you can use the Clockwise/Spiral Rule made by David Anderson; but not to confuse with Anderson`s Rule made by Ross J. Anderson, which is something quite distinct.
simple mnemonic:
type pointer <- * -> pointee name
I like to think of int *i as declaring "the dereference of i is int"; in this sense, const int *i means "the deref of i is const int", while int *const i means "deref of const i is int".
(the one danger of thinking like this is it may lead to favoring int const *i style of declaration, which people might hate/disallow)
Nobody has mentioned the system underlying declarations which Kernighan and Ritchie pointed out in their C book:
Declarations mimic expressions.
I'll repeat this because it so essential and gives a clear strategy to parse even the most complicated declarations:
Declarations mimic expressions.
The declarations contain the same operators as expressions the declared identifier can appear in later, with the same priority they have in expressions. This is why the "clockwise spiral rule" is wrong: The evaluation order is strictly determined by the operator precedences, with complete disregard for left, right or rotational directions.
Here are a few Examples, in order of increasing complexity:
int i;: When i is used as-is, it is an expression of type int. Therefore, i is an int.
int *p;: When p is dereferenced with *, the expression is of type int. Therefore, p is a pointer to int.
const int *p;: When p is dereferenced with *, the expression is of type const int. Therefore, p is a pointer to const int.
int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type int. Therefore, p is a const pointer to int.
const int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type const int. Therefore, p is a const pointer to const int.
So far we didn't have any issues with operator precedence yet: We simply evaluated right-to-left. This changes when we have fun with arrays of pointers and pointers to arrays. You may want to have a cheat sheet open.
int a[3];: When we apply the array indexing operator to a, the result is an int. Therefore, a is an array of int.
int *a[3];: Here the indexing operator has higher precedence, so we apply it first: When we apply the array indexing operator to a, the result is an int *. Therefore, a is an array of pointers to int. This is not uncommon.
int (*a)[3];: Here the operator precedence is overridden by round parentheses, exactly as in any expression. Consequently, we dereference first. We know now that a is a pointer to some type. *a, the dereferenced pointer, is an expression of that type. When we apply the array indexing operator to *a, we obtain a plain int, which means that *a is an array of three ints, and a is a pointer to that array. This is fairly uncommon outside of C++ templates, which is why the operator precedences are not catering to this case. Note how the use of such a pointer is the model for its declaration: int i = (*a)[1];. The parentheses are mandatory to dereference first.
int (*a)[3][2];: There is nothing preventing anybody from having pointers to multi-dimensional arrays, one case where circular spiral clockwise advice becomes obvious nonsense.
A thing that sometimes comes up in real life are function pointers. We need parentheses there as well because the function call operator (operator()() in C++, simple syntax rule in C) has higher priority than the dereferencing operator*(), again because it's more common to have functions returning pointers than pointers to functions:
int *f();: Function call first, so f is a function. The call must be dereferenced to result in an int, so the return value is a pointer to int. Usage: int i = *f();.
int (*fp)();: Parentheses change order of operator application. Because we must dereference first we know that fp is a pointer to something. Because we can apply the function call operator to *fp we know (in C) that fp is a pointer to a function; in C++ we only know that it is something for which operator()() is defined. Since the call takes no parameters and returns an int, fp is in C++ a pointer to a function with that signature. (In C, an empty parameter list indicates that nothing is known about the parameters, but future C specifications may forbid that obsolete use.)
int *(*fp)();: Of course we can return pointers to int from a function pointed to.
int (*(*fp)())[3];: Dereference first, hence a pointer; apply function call operator next, hence a pointer to function; dereference the return value again, hence a pointer to a function returning a pointer; apply the indexing operator to that: pointer to function returning pointer to array. The result is an int, hence pointer to function returning pointer to array of ints.-
All parentheses are necessary: As discussed, we must prioritize dereferencing of the function pointer with (*fp) before anything else happens. Obviously, we need the function call; and since the function returns a pointer to an array (not to its first element!), we must dereference that as well before we can index it. I admit that I wrote a test program to check this because I wasn't sure, even with this fool-proof method ;-). Here it is:
#include <iostream>
using namespace std;
int (*f())[3]
{
static int arr[3] = {1,2,3};
return &arr;
}
int (*(*fp)())[3] = &f;
int main()
{
for(int i=0; i<3; i++)
{
cout << (*(*fp)())[i] << endl;
}
}
Note how beautifully the declaration mimics the expression!
Lot of people answered correctly I will just organize well here and put some Extra info which is missing in given Answers.
Const is keyword in C language also known as qualifier. Const can
applied to the declaration of any variable to specify that it's value
will not changed
const int a=3,b;
a=4; // give error
b=5; // give error as b is also const int
you have to intialize while declaring itself as no way to assign
it afterwards.
How to read ?
just read from right to left every statement works smoothly
3 main things
type a. p is ptr to const int
type b. p is const ptr to int
type c. p is const ptr to const int
[Error]
if * comes before int
two types
1. const int *
2. const const int *
we look first
Major type 1. const int*
ways to arrange 3 things at 3 places 3!=6
i. * at start
*const int p [Error]
*int const p [Error]
ii. const at start
const int *p type a. p is ptr to const int
const *int p [Error]
iii. int at start
int const *p type a.
int * const p type b. p is const ptr to int
Major type 2. const const int*
ways to arrange 4 things at 4 places in which 2 are alike 4!/2!=12
i. * at start
* int const const p [Error]
* const int const p [Error]
* const const int p [Error]
ii. int at start
int const const *p type a. p is ptr to const int
int const * const p type c. p is const ptr to const int
int * const const p type b. p is const ptr to int
iii. const at start
const const int *p type a.
const const * int p [Error]
const int const *p type a.
const int * const p type c.
const * int const p [Error]
const * const int p [Error]
squeezing all in one
type a. p is ptr to const int (5)
const int *p
int const *p
int const const *p
const const int *p
const int const *p
type b. p is const ptr to int (2)
int * const p
int * const const p;
type c. p is const ptr to const int (2)
int const * const p
const int * const p
just little calculation
1. const int * p total arrangemets (6) [Errors] (3)
2. const const int * p total arrangemets (12) [Errors] (6)
little Extra
int const * p,p2 ;
here p is ptr to const int (type a.)
but p2 is just const int please note that it is not ptr
int * const p,p2 ;
similarly
here p is const ptr to int (type b.)
but p2 is just int not even cost int
int const * const p,p2 ;
here p is const ptr to const int (type c.)
but p2 is just const int.
Finished
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.
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.
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:
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.
Like pretty much everyone pointed out:
What’s the difference between const X* p, X* const p and const X* const p?
You have to read pointer declarations
right-to-left.
const X* p means "p points to an X that is const": the X object can't be changed via p.
X* const p means "p is a const pointer to an X that is non-const": you can't change the pointer p itself, but you can change the X object via p.
const X* const p means "p is a const pointer to an X that is const": you can't change the pointer p itself, nor can you change the X object via p.
Constant reference:
A reference to a variable (here int), which is constant. We pass the variable as a reference mainly, because references are smaller in size than the actual value, but there is a side effect and that is because it is like an alias to the actual variable. We may accidentally change the main variable through our full access to the alias, so we make it constant to prevent this side effect.
int var0 = 0;
const int &ptr1 = var0;
ptr1 = 8; // Error
var0 = 6; // OK
Constant pointers
Once a constant pointer points to a variable then it cannot point to any other variable.
int var1 = 1;
int var2 = 0;
int *const ptr2 = &var1;
ptr2 = &var2; // Error
Pointer to constant
A pointer through which one cannot change the value of a variable it points is known as a pointer to constant.
int const * ptr3 = &var2;
*ptr3 = 4; // Error
Constant pointer to a constant
A constant pointer to a constant is a pointer that can neither change the address it's pointing to and nor can it change the value kept at that address.
int var3 = 0;
int var4 = 0;
const int * const ptr4 = &var3;
*ptr4 = 1; // Error
ptr4 = &var4; // Error
This question shows precisely why I like to do things the way I mentioned in my question is const after type id acceptable?
In short, I find the easiest way to remember the rule is that the "const" goes after the thing it applies to. So in your question, "int const *" means that the int is constant, while "int * const" would mean that the pointer is constant.
If someone decides to put it at the very front (eg: "const int *"), as a special exception in that case it applies to the thing after it.
Many people like to use that special exception because they think it looks nicer. I dislike it, because it is an exception, and thus confuses things.
The general rule is that the const keyword applies to what precedes it immediately. Exception, a starting const applies to what follows.
const int* is the same as int const* and means "pointer to constant int".
const int* const is the same as int 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?
Simple Use of const.
The simplest use is to declare a named constant. To do this, one declares a constant as if it was a variable but add const before it. One has to initialize it immediately in the constructor because, of course, one cannot set the value later as that would be altering it. For example:
const int Constant1=96;
will create an integer constant, unimaginatively called Constant1, with the value 96.
Such constants are useful for parameters which are used in the program but are do not need to be changed after the program is compiled. It has an advantage for programmers over the C preprocessor #define command in that it is understood & used by the compiler itself, not just substituted into the program text by the preprocessor before reaching the main compiler, so error messages are much more helpful.
It also works with pointers but one has to be careful where const to determine whether the pointer or what it points to is constant or both. For example:
const int * Constant2
declares that Constant2 is variable pointer to a constant integer and:
int const * Constant2
is an alternative syntax which does the same, whereas
int * const Constant3
declares that Constant3 is constant pointer to a variable integer and
int const * const Constant4
declares that Constant4 is constant pointer to a constant integer. Basically ‘const’ applies to whatever is on its immediate left (other than if there is nothing there in which case it applies to whatever is its immediate right).
ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
It's simple but tricky. Please note that we can apply the const qualifier to 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 pointer p 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-only
const int *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p 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 to int const *p
int const *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p here is read-only, the declaration and definition should be in same place.
I had the same doubt as you until I came across this book by the C++ Guru Scott Meyers. Refer the third Item in this book where he talks in details about using const.
Just follow this advice
If the word const appears to the left of the asterisk, what's pointed to is constant
If the word const appears to the right of the asterisk, the pointer itself is constant
If const appears on both sides, both are constant
To remember in easy way :
If const is before * then value is constant.
If const is after * then address is constant.
if const are available both before and after * then both value and address are constant.
e.g.
int * const var; //here address is constant.
int const * var; //here value is constant.
int const * const var; // both value and address are constant.
The C and C++ declaration syntax has repeatedly been described as a failed experiment, by the original designers.
Instead, let's name the type “pointer to Type”; I’ll call it Ptr_:
template< class Type >
using Ptr_ = Type*;
Now Ptr_<char> is a pointer to char.
Ptr_<const char> is a pointer to const char.
And const Ptr_<const char> is a const pointer to const char.
For me, the position of const i.e. whether it appears to the LEFT or RIGHT or on both LEFT and RIGHT relative to the * helps me figure out the actual meaning.
A const to the LEFT of * indicates that the object pointed by the pointer is a const object.
A const to the RIGHT of * indicates that the pointer is a const pointer.
The following table is taken from Stanford CS106L Standard C++ Programming Laboratory Course Reader.
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 a TYPE 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 drew an image below to explain this, maybe helpful.
int const v and const int v are identical.
The const with the int on either sides will make pointer to constant int:
const int *ptr=&i;
or:
int const *ptr=&i;
const after * will make constant pointer to int:
int *const ptr=&i;
In this case all of these are pointer to constant integer, but none of these are constant pointer:
const int *ptr1=&i, *ptr2=&j;
In this case all are pointer to constant integer and ptr2 is constant pointer to constant integer. But ptr1 is not constant pointer:
int const *ptr1=&i, *const ptr2=&j;
if const is to the left of *, it refers to the value (it doesn't matter whether it's const int or int const)
if const is to the right of *, it refers to the pointer itself
it can be both at the same time
An important point: const int *p does not mean the value you are referring to is constant!!. It means that you can't change it through that pointer (meaning, you can't assign $*p = ...`). The value itself may be changed in other ways. Eg
int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error
This is meant to be used mostly in function signatures, to guarantee that the function can't accidentally change the arguments passed.
This mostly addresses the second line: best practices, assignments, function parameters etc.
General practice. Try to make everything const that you can. Or to put that another way, make everything const to begin with, and then remove exactly the minimum set of consts necessary to allow the program to function. This will be a big help in attaining const-correctness, and will help ensure that subtle bugs don't get introduced when people try and assign into things they're not supposed to modify.
Avoid const_cast<> like the plague. There are one or two legitimate use cases for it, but they are very few and far between. If you're trying to change a const object, you'll do a lot better to find whoever declared it const in the first pace and talk the matter over with them to reach a consensus as to what should happen.
Which leads very neatly into assignments. You can assign into something only if it is non-const. If you want to assign into something that is const, see above. Remember that in the declarations int const *foo; and int * const bar; different things are const - other answers here have covered that issue admirably, so I won't go into it.
Function parameters:
Pass by value: e.g. void func(int param) you don't care one way or the other at the calling site. The argument can be made that there are use cases for declaring the function as void func(int const param) but that has no effect on the caller, only on the function itself, in that whatever value is passed cannot be changed by the function during the call.
Pass by reference: e.g. void func(int ¶m) Now it does make a difference. As just declared func is allowed to change param, and any calling site should be ready to deal with the consequences. Changing the declaration to void func(int const ¶m) changes the contract, and guarantees that func can now not change param, meaning what is passed in is what will come back out. As other have noted this is very useful for cheaply passing a large object that you don't want to change. Passing a reference is a lot cheaper than passing a large object by value.
Pass by pointer: e.g. void func(int *param) and void func(int const *param) These two are pretty much synonymous with their reference counterparts, with the caveat that the called function now needs to check for nullptr unless some other contractual guarantee assures func that it will never receive a nullptr in param.
Opinion piece on that topic. Proving correctness in a case like this is hellishly difficult, it's just too damn easy to make a mistake. So don't take chances, and always check pointer parameters for nullptr. You will save yourself pain and suffering and hard to find bugs in the long term. And as for the cost of the check, it's dirt cheap, and in cases where the static analysis built into the compiler can manage it, the optimizer will elide it anyway. Turn on Link Time Code Generation for MSVC, or WOPR (I think) for GCC, and you'll get it program wide, i.e. even in function calls that cross a source code module boundary.
At the end of the day all of the above makes a very solid case to always prefer references to pointers. They're just safer all round.
Just for the sake of completeness for C following the others explanations, not sure for C++.
pp - pointer to pointer
p - pointer
data - the thing pointed, in examples x
bold - read-only variable
Pointer
p data - int *p;
p data - int const *p;
p data - int * const p;
p data - int const * const p;
Pointer to pointer
pp p data - int **pp;
pp p data - int ** const pp;
pp p data - int * const *pp;
pp p data - int const **pp;
pp p data - int * const * const pp;
pp p data - int const ** const pp;
pp p data - int const * const *pp;
pp p data - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
N-levels of Dereference
Just keep going, but may the humanity excommunicate you.
int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;
printf("%d \n", ****pppp);
const int* - pointer to constant int object.
You can change the value of the pointer; you can not change the value of the int object, the pointer points to.
const int * const - constant pointer to constant int object.
You can not change the value of the pointer nor the value of the int object the pointer points to.
int const * - pointer to constant int object.
This statement is equivalent to 1. const int* - You can change the value of the pointer but you can not change the value of the int object, the pointer points to.
Actually, there is a 4th option:
int * const - constant pointer to int object.
You can change the value of the object the pointer points to but you can not change the value of the pointer itself. The pointer will always point to the same int object but this value of this int object can be changed.
If you want to determine a certain type of C or C++ construct you can use the Clockwise/Spiral Rule made by David Anderson; but not to confuse with Anderson`s Rule made by Ross J. Anderson, which is something quite distinct.
simple mnemonic:
type pointer <- * -> pointee name
I like to think of int *i as declaring "the dereference of i is int"; in this sense, const int *i means "the deref of i is const int", while int *const i means "deref of const i is int".
(the one danger of thinking like this is it may lead to favoring int const *i style of declaration, which people might hate/disallow)
Nobody has mentioned the system underlying declarations which Kernighan and Ritchie pointed out in their C book:
Declarations mimic expressions.
I'll repeat this because it so essential and gives a clear strategy to parse even the most complicated declarations:
Declarations mimic expressions.
The declarations contain the same operators as expressions the declared identifier can appear in later, with the same priority they have in expressions. This is why the "clockwise spiral rule" is wrong: The evaluation order is strictly determined by the operator precedences, with complete disregard for left, right or rotational directions.
Here are a few Examples, in order of increasing complexity:
int i;: When i is used as-is, it is an expression of type int. Therefore, i is an int.
int *p;: When p is dereferenced with *, the expression is of type int. Therefore, p is a pointer to int.
const int *p;: When p is dereferenced with *, the expression is of type const int. Therefore, p is a pointer to const int.
int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type int. Therefore, p is a const pointer to int.
const int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type const int. Therefore, p is a const pointer to const int.
So far we didn't have any issues with operator precedence yet: We simply evaluated right-to-left. This changes when we have fun with arrays of pointers and pointers to arrays. You may want to have a cheat sheet open.
int a[3];: When we apply the array indexing operator to a, the result is an int. Therefore, a is an array of int.
int *a[3];: Here the indexing operator has higher precedence, so we apply it first: When we apply the array indexing operator to a, the result is an int *. Therefore, a is an array of pointers to int. This is not uncommon.
int (*a)[3];: Here the operator precedence is overridden by round parentheses, exactly as in any expression. Consequently, we dereference first. We know now that a is a pointer to some type. *a, the dereferenced pointer, is an expression of that type. When we apply the array indexing operator to *a, we obtain a plain int, which means that *a is an array of three ints, and a is a pointer to that array. This is fairly uncommon outside of C++ templates, which is why the operator precedences are not catering to this case. Note how the use of such a pointer is the model for its declaration: int i = (*a)[1];. The parentheses are mandatory to dereference first.
int (*a)[3][2];: There is nothing preventing anybody from having pointers to multi-dimensional arrays, one case where circular spiral clockwise advice becomes obvious nonsense.
A thing that sometimes comes up in real life are function pointers. We need parentheses there as well because the function call operator (operator()() in C++, simple syntax rule in C) has higher priority than the dereferencing operator*(), again because it's more common to have functions returning pointers than pointers to functions:
int *f();: Function call first, so f is a function. The call must be dereferenced to result in an int, so the return value is a pointer to int. Usage: int i = *f();.
int (*fp)();: Parentheses change order of operator application. Because we must dereference first we know that fp is a pointer to something. Because we can apply the function call operator to *fp we know (in C) that fp is a pointer to a function; in C++ we only know that it is something for which operator()() is defined. Since the call takes no parameters and returns an int, fp is in C++ a pointer to a function with that signature. (In C, an empty parameter list indicates that nothing is known about the parameters, but future C specifications may forbid that obsolete use.)
int *(*fp)();: Of course we can return pointers to int from a function pointed to.
int (*(*fp)())[3];: Dereference first, hence a pointer; apply function call operator next, hence a pointer to function; dereference the return value again, hence a pointer to a function returning a pointer; apply the indexing operator to that: pointer to function returning pointer to array. The result is an int, hence pointer to function returning pointer to array of ints.-
All parentheses are necessary: As discussed, we must prioritize dereferencing of the function pointer with (*fp) before anything else happens. Obviously, we need the function call; and since the function returns a pointer to an array (not to its first element!), we must dereference that as well before we can index it. I admit that I wrote a test program to check this because I wasn't sure, even with this fool-proof method ;-). Here it is:
#include <iostream>
using namespace std;
int (*f())[3]
{
static int arr[3] = {1,2,3};
return &arr;
}
int (*(*fp)())[3] = &f;
int main()
{
for(int i=0; i<3; i++)
{
cout << (*(*fp)())[i] << endl;
}
}
Note how beautifully the declaration mimics the expression!
Lot of people answered correctly I will just organize well here and put some Extra info which is missing in given Answers.
Const is keyword in C language also known as qualifier. Const can
applied to the declaration of any variable to specify that it's value
will not changed
const int a=3,b;
a=4; // give error
b=5; // give error as b is also const int
you have to intialize while declaring itself as no way to assign
it afterwards.
How to read ?
just read from right to left every statement works smoothly
3 main things
type a. p is ptr to const int
type b. p is const ptr to int
type c. p is const ptr to const int
[Error]
if * comes before int
two types
1. const int *
2. const const int *
we look first
Major type 1. const int*
ways to arrange 3 things at 3 places 3!=6
i. * at start
*const int p [Error]
*int const p [Error]
ii. const at start
const int *p type a. p is ptr to const int
const *int p [Error]
iii. int at start
int const *p type a.
int * const p type b. p is const ptr to int
Major type 2. const const int*
ways to arrange 4 things at 4 places in which 2 are alike 4!/2!=12
i. * at start
* int const const p [Error]
* const int const p [Error]
* const const int p [Error]
ii. int at start
int const const *p type a. p is ptr to const int
int const * const p type c. p is const ptr to const int
int * const const p type b. p is const ptr to int
iii. const at start
const const int *p type a.
const const * int p [Error]
const int const *p type a.
const int * const p type c.
const * int const p [Error]
const * const int p [Error]
squeezing all in one
type a. p is ptr to const int (5)
const int *p
int const *p
int const const *p
const const int *p
const int const *p
type b. p is const ptr to int (2)
int * const p
int * const const p;
type c. p is const ptr to const int (2)
int const * const p
const int * const p
just little calculation
1. const int * p total arrangemets (6) [Errors] (3)
2. const const int * p total arrangemets (12) [Errors] (6)
little Extra
int const * p,p2 ;
here p is ptr to const int (type a.)
but p2 is just const int please note that it is not ptr
int * const p,p2 ;
similarly
here p is const ptr to int (type b.)
but p2 is just int not even cost int
int const * const p,p2 ;
here p is const ptr to const int (type c.)
but p2 is just const int.
Finished
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.
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.
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:
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.
Like pretty much everyone pointed out:
What’s the difference between const X* p, X* const p and const X* const p?
You have to read pointer declarations
right-to-left.
const X* p means "p points to an X that is const": the X object can't be changed via p.
X* const p means "p is a const pointer to an X that is non-const": you can't change the pointer p itself, but you can change the X object via p.
const X* const p means "p is a const pointer to an X that is const": you can't change the pointer p itself, nor can you change the X object via p.
Constant reference:
A reference to a variable (here int), which is constant. We pass the variable as a reference mainly, because references are smaller in size than the actual value, but there is a side effect and that is because it is like an alias to the actual variable. We may accidentally change the main variable through our full access to the alias, so we make it constant to prevent this side effect.
int var0 = 0;
const int &ptr1 = var0;
ptr1 = 8; // Error
var0 = 6; // OK
Constant pointers
Once a constant pointer points to a variable then it cannot point to any other variable.
int var1 = 1;
int var2 = 0;
int *const ptr2 = &var1;
ptr2 = &var2; // Error
Pointer to constant
A pointer through which one cannot change the value of a variable it points is known as a pointer to constant.
int const * ptr3 = &var2;
*ptr3 = 4; // Error
Constant pointer to a constant
A constant pointer to a constant is a pointer that can neither change the address it's pointing to and nor can it change the value kept at that address.
int var3 = 0;
int var4 = 0;
const int * const ptr4 = &var3;
*ptr4 = 1; // Error
ptr4 = &var4; // Error
This question shows precisely why I like to do things the way I mentioned in my question is const after type id acceptable?
In short, I find the easiest way to remember the rule is that the "const" goes after the thing it applies to. So in your question, "int const *" means that the int is constant, while "int * const" would mean that the pointer is constant.
If someone decides to put it at the very front (eg: "const int *"), as a special exception in that case it applies to the thing after it.
Many people like to use that special exception because they think it looks nicer. I dislike it, because it is an exception, and thus confuses things.
The general rule is that the const keyword applies to what precedes it immediately. Exception, a starting const applies to what follows.
const int* is the same as int const* and means "pointer to constant int".
const int* const is the same as int 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?
Simple Use of const.
The simplest use is to declare a named constant. To do this, one declares a constant as if it was a variable but add const before it. One has to initialize it immediately in the constructor because, of course, one cannot set the value later as that would be altering it. For example:
const int Constant1=96;
will create an integer constant, unimaginatively called Constant1, with the value 96.
Such constants are useful for parameters which are used in the program but are do not need to be changed after the program is compiled. It has an advantage for programmers over the C preprocessor #define command in that it is understood & used by the compiler itself, not just substituted into the program text by the preprocessor before reaching the main compiler, so error messages are much more helpful.
It also works with pointers but one has to be careful where const to determine whether the pointer or what it points to is constant or both. For example:
const int * Constant2
declares that Constant2 is variable pointer to a constant integer and:
int const * Constant2
is an alternative syntax which does the same, whereas
int * const Constant3
declares that Constant3 is constant pointer to a variable integer and
int const * const Constant4
declares that Constant4 is constant pointer to a constant integer. Basically ‘const’ applies to whatever is on its immediate left (other than if there is nothing there in which case it applies to whatever is its immediate right).
ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
It's simple but tricky. Please note that we can apply the const qualifier to 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 pointer p 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-only
const int *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p 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 to int const *p
int const *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p here is read-only, the declaration and definition should be in same place.
I had the same doubt as you until I came across this book by the C++ Guru Scott Meyers. Refer the third Item in this book where he talks in details about using const.
Just follow this advice
If the word const appears to the left of the asterisk, what's pointed to is constant
If the word const appears to the right of the asterisk, the pointer itself is constant
If const appears on both sides, both are constant
To remember in easy way :
If const is before * then value is constant.
If const is after * then address is constant.
if const are available both before and after * then both value and address are constant.
e.g.
int * const var; //here address is constant.
int const * var; //here value is constant.
int const * const var; // both value and address are constant.
The C and C++ declaration syntax has repeatedly been described as a failed experiment, by the original designers.
Instead, let's name the type “pointer to Type”; I’ll call it Ptr_:
template< class Type >
using Ptr_ = Type*;
Now Ptr_<char> is a pointer to char.
Ptr_<const char> is a pointer to const char.
And const Ptr_<const char> is a const pointer to const char.
For me, the position of const i.e. whether it appears to the LEFT or RIGHT or on both LEFT and RIGHT relative to the * helps me figure out the actual meaning.
A const to the LEFT of * indicates that the object pointed by the pointer is a const object.
A const to the RIGHT of * indicates that the pointer is a const pointer.
The following table is taken from Stanford CS106L Standard C++ Programming Laboratory Course Reader.
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 a TYPE 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 drew an image below to explain this, maybe helpful.
int const v and const int v are identical.
The const with the int on either sides will make pointer to constant int:
const int *ptr=&i;
or:
int const *ptr=&i;
const after * will make constant pointer to int:
int *const ptr=&i;
In this case all of these are pointer to constant integer, but none of these are constant pointer:
const int *ptr1=&i, *ptr2=&j;
In this case all are pointer to constant integer and ptr2 is constant pointer to constant integer. But ptr1 is not constant pointer:
int const *ptr1=&i, *const ptr2=&j;
if const is to the left of *, it refers to the value (it doesn't matter whether it's const int or int const)
if const is to the right of *, it refers to the pointer itself
it can be both at the same time
An important point: const int *p does not mean the value you are referring to is constant!!. It means that you can't change it through that pointer (meaning, you can't assign $*p = ...`). The value itself may be changed in other ways. Eg
int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error
This is meant to be used mostly in function signatures, to guarantee that the function can't accidentally change the arguments passed.
This mostly addresses the second line: best practices, assignments, function parameters etc.
General practice. Try to make everything const that you can. Or to put that another way, make everything const to begin with, and then remove exactly the minimum set of consts necessary to allow the program to function. This will be a big help in attaining const-correctness, and will help ensure that subtle bugs don't get introduced when people try and assign into things they're not supposed to modify.
Avoid const_cast<> like the plague. There are one or two legitimate use cases for it, but they are very few and far between. If you're trying to change a const object, you'll do a lot better to find whoever declared it const in the first pace and talk the matter over with them to reach a consensus as to what should happen.
Which leads very neatly into assignments. You can assign into something only if it is non-const. If you want to assign into something that is const, see above. Remember that in the declarations int const *foo; and int * const bar; different things are const - other answers here have covered that issue admirably, so I won't go into it.
Function parameters:
Pass by value: e.g. void func(int param) you don't care one way or the other at the calling site. The argument can be made that there are use cases for declaring the function as void func(int const param) but that has no effect on the caller, only on the function itself, in that whatever value is passed cannot be changed by the function during the call.
Pass by reference: e.g. void func(int ¶m) Now it does make a difference. As just declared func is allowed to change param, and any calling site should be ready to deal with the consequences. Changing the declaration to void func(int const ¶m) changes the contract, and guarantees that func can now not change param, meaning what is passed in is what will come back out. As other have noted this is very useful for cheaply passing a large object that you don't want to change. Passing a reference is a lot cheaper than passing a large object by value.
Pass by pointer: e.g. void func(int *param) and void func(int const *param) These two are pretty much synonymous with their reference counterparts, with the caveat that the called function now needs to check for nullptr unless some other contractual guarantee assures func that it will never receive a nullptr in param.
Opinion piece on that topic. Proving correctness in a case like this is hellishly difficult, it's just too damn easy to make a mistake. So don't take chances, and always check pointer parameters for nullptr. You will save yourself pain and suffering and hard to find bugs in the long term. And as for the cost of the check, it's dirt cheap, and in cases where the static analysis built into the compiler can manage it, the optimizer will elide it anyway. Turn on Link Time Code Generation for MSVC, or WOPR (I think) for GCC, and you'll get it program wide, i.e. even in function calls that cross a source code module boundary.
At the end of the day all of the above makes a very solid case to always prefer references to pointers. They're just safer all round.
Just for the sake of completeness for C following the others explanations, not sure for C++.
pp - pointer to pointer
p - pointer
data - the thing pointed, in examples x
bold - read-only variable
Pointer
p data - int *p;
p data - int const *p;
p data - int * const p;
p data - int const * const p;
Pointer to pointer
pp p data - int **pp;
pp p data - int ** const pp;
pp p data - int * const *pp;
pp p data - int const **pp;
pp p data - int * const * const pp;
pp p data - int const ** const pp;
pp p data - int const * const *pp;
pp p data - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
N-levels of Dereference
Just keep going, but may the humanity excommunicate you.
int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;
printf("%d \n", ****pppp);
const int* - pointer to constant int object.
You can change the value of the pointer; you can not change the value of the int object, the pointer points to.
const int * const - constant pointer to constant int object.
You can not change the value of the pointer nor the value of the int object the pointer points to.
int const * - pointer to constant int object.
This statement is equivalent to 1. const int* - You can change the value of the pointer but you can not change the value of the int object, the pointer points to.
Actually, there is a 4th option:
int * const - constant pointer to int object.
You can change the value of the object the pointer points to but you can not change the value of the pointer itself. The pointer will always point to the same int object but this value of this int object can be changed.
If you want to determine a certain type of C or C++ construct you can use the Clockwise/Spiral Rule made by David Anderson; but not to confuse with Anderson`s Rule made by Ross J. Anderson, which is something quite distinct.
simple mnemonic:
type pointer <- * -> pointee name
I like to think of int *i as declaring "the dereference of i is int"; in this sense, const int *i means "the deref of i is const int", while int *const i means "deref of const i is int".
(the one danger of thinking like this is it may lead to favoring int const *i style of declaration, which people might hate/disallow)
Nobody has mentioned the system underlying declarations which Kernighan and Ritchie pointed out in their C book:
Declarations mimic expressions.
I'll repeat this because it so essential and gives a clear strategy to parse even the most complicated declarations:
Declarations mimic expressions.
The declarations contain the same operators as expressions the declared identifier can appear in later, with the same priority they have in expressions. This is why the "clockwise spiral rule" is wrong: The evaluation order is strictly determined by the operator precedences, with complete disregard for left, right or rotational directions.
Here are a few Examples, in order of increasing complexity:
int i;: When i is used as-is, it is an expression of type int. Therefore, i is an int.
int *p;: When p is dereferenced with *, the expression is of type int. Therefore, p is a pointer to int.
const int *p;: When p is dereferenced with *, the expression is of type const int. Therefore, p is a pointer to const int.
int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type int. Therefore, p is a const pointer to int.
const int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type const int. Therefore, p is a const pointer to const int.
So far we didn't have any issues with operator precedence yet: We simply evaluated right-to-left. This changes when we have fun with arrays of pointers and pointers to arrays. You may want to have a cheat sheet open.
int a[3];: When we apply the array indexing operator to a, the result is an int. Therefore, a is an array of int.
int *a[3];: Here the indexing operator has higher precedence, so we apply it first: When we apply the array indexing operator to a, the result is an int *. Therefore, a is an array of pointers to int. This is not uncommon.
int (*a)[3];: Here the operator precedence is overridden by round parentheses, exactly as in any expression. Consequently, we dereference first. We know now that a is a pointer to some type. *a, the dereferenced pointer, is an expression of that type. When we apply the array indexing operator to *a, we obtain a plain int, which means that *a is an array of three ints, and a is a pointer to that array. This is fairly uncommon outside of C++ templates, which is why the operator precedences are not catering to this case. Note how the use of such a pointer is the model for its declaration: int i = (*a)[1];. The parentheses are mandatory to dereference first.
int (*a)[3][2];: There is nothing preventing anybody from having pointers to multi-dimensional arrays, one case where circular spiral clockwise advice becomes obvious nonsense.
A thing that sometimes comes up in real life are function pointers. We need parentheses there as well because the function call operator (operator()() in C++, simple syntax rule in C) has higher priority than the dereferencing operator*(), again because it's more common to have functions returning pointers than pointers to functions:
int *f();: Function call first, so f is a function. The call must be dereferenced to result in an int, so the return value is a pointer to int. Usage: int i = *f();.
int (*fp)();: Parentheses change order of operator application. Because we must dereference first we know that fp is a pointer to something. Because we can apply the function call operator to *fp we know (in C) that fp is a pointer to a function; in C++ we only know that it is something for which operator()() is defined. Since the call takes no parameters and returns an int, fp is in C++ a pointer to a function with that signature. (In C, an empty parameter list indicates that nothing is known about the parameters, but future C specifications may forbid that obsolete use.)
int *(*fp)();: Of course we can return pointers to int from a function pointed to.
int (*(*fp)())[3];: Dereference first, hence a pointer; apply function call operator next, hence a pointer to function; dereference the return value again, hence a pointer to a function returning a pointer; apply the indexing operator to that: pointer to function returning pointer to array. The result is an int, hence pointer to function returning pointer to array of ints.-
All parentheses are necessary: As discussed, we must prioritize dereferencing of the function pointer with (*fp) before anything else happens. Obviously, we need the function call; and since the function returns a pointer to an array (not to its first element!), we must dereference that as well before we can index it. I admit that I wrote a test program to check this because I wasn't sure, even with this fool-proof method ;-). Here it is:
#include <iostream>
using namespace std;
int (*f())[3]
{
static int arr[3] = {1,2,3};
return &arr;
}
int (*(*fp)())[3] = &f;
int main()
{
for(int i=0; i<3; i++)
{
cout << (*(*fp)())[i] << endl;
}
}
Note how beautifully the declaration mimics the expression!
Lot of people answered correctly I will just organize well here and put some Extra info which is missing in given Answers.
Const is keyword in C language also known as qualifier. Const can
applied to the declaration of any variable to specify that it's value
will not changed
const int a=3,b;
a=4; // give error
b=5; // give error as b is also const int
you have to intialize while declaring itself as no way to assign
it afterwards.
How to read ?
just read from right to left every statement works smoothly
3 main things
type a. p is ptr to const int
type b. p is const ptr to int
type c. p is const ptr to const int
[Error]
if * comes before int
two types
1. const int *
2. const const int *
we look first
Major type 1. const int*
ways to arrange 3 things at 3 places 3!=6
i. * at start
*const int p [Error]
*int const p [Error]
ii. const at start
const int *p type a. p is ptr to const int
const *int p [Error]
iii. int at start
int const *p type a.
int * const p type b. p is const ptr to int
Major type 2. const const int*
ways to arrange 4 things at 4 places in which 2 are alike 4!/2!=12
i. * at start
* int const const p [Error]
* const int const p [Error]
* const const int p [Error]
ii. int at start
int const const *p type a. p is ptr to const int
int const * const p type c. p is const ptr to const int
int * const const p type b. p is const ptr to int
iii. const at start
const const int *p type a.
const const * int p [Error]
const int const *p type a.
const int * const p type c.
const * int const p [Error]
const * const int p [Error]
squeezing all in one
type a. p is ptr to const int (5)
const int *p
int const *p
int const const *p
const const int *p
const int const *p
type b. p is const ptr to int (2)
int * const p
int * const const p;
type c. p is const ptr to const int (2)
int const * const p
const int * const p
just little calculation
1. const int * p total arrangemets (6) [Errors] (3)
2. const const int * p total arrangemets (12) [Errors] (6)
little Extra
int const * p,p2 ;
here p is ptr to const int (type a.)
but p2 is just const int please note that it is not ptr
int * const p,p2 ;
similarly
here p is const ptr to int (type b.)
but p2 is just int not even cost int
int const * const p,p2 ;
here p is const ptr to const int (type c.)
but p2 is just const int.
Finished
Say I have an array of pointers to integers (i.e each element is a pointer to int)
int** ptrArray;
and I want to prevent changes to the integers pointed by the entries of the array
where do I need to put the const?
1. const int ** ptrArray
2. int const ** ptrArray
3. int *const* ptrArray
4. int ** const ptrArray
are there any rules for this?
like "first const protects data", "second const protects the pointer" and so on?
Is there any logic behind the location of the const?
any connection between where to put and what it protect?
this is very a confusing issue for me and I would really appriciate if someone can give me any guide or link to where I can read more about how and where to use the const based on what I want to protect (in case I need to use const in a 3dimensional array or so)
are there any rules for this? like "first const protects data", "second const protects the pointer" and so on?
Yes: const applies to the left, unless there's nothing there, then it applies to the right.
Therefore, this:
int const** ptrArray;
but, as a special case, this is equivalent:
const int** ptrArray;
This means the common pattern const int x; is actually int const x; in disguise.
You read modifiers starting from the variable.
e.g.
3. int *const* ptrArray
ptrArray is a variable.
*ptrArray denotes a pointer to something
const* ptrArray denotes a pointer to something that is constant
*const* ptrArray denotes a pointer to a constant pointer to something
So int *const* ptrArray; declares a pointer to a constant pointer to a (nonconstant) int
The final technicality you'd need to know to understand all the examples is that int const and const int are two different ways to denote the same type.
http://cdecl.org reports (more or less, one was an invalid syntax error):
const int ** ptrArray: declare ptrArray as pointer to pointer to const int
int const ** ptrArray: declare ptrArray as pointer to pointer to const int
int * const * ptrArray: declare ptrArray as pointer to const pointer to int
int ** const ptrArray: declare ptrArray as const pointer to pointer to int
So you're looking for either of:
const int ** ptrArray
int const ** ptrArray
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.
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.
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:
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.
Like pretty much everyone pointed out:
What’s the difference between const X* p, X* const p and const X* const p?
You have to read pointer declarations
right-to-left.
const X* p means "p points to an X that is const": the X object can't be changed via p.
X* const p means "p is a const pointer to an X that is non-const": you can't change the pointer p itself, but you can change the X object via p.
const X* const p means "p is a const pointer to an X that is const": you can't change the pointer p itself, nor can you change the X object via p.
Constant reference:
A reference to a variable (here int), which is constant. We pass the variable as a reference mainly, because references are smaller in size than the actual value, but there is a side effect and that is because it is like an alias to the actual variable. We may accidentally change the main variable through our full access to the alias, so we make it constant to prevent this side effect.
int var0 = 0;
const int &ptr1 = var0;
ptr1 = 8; // Error
var0 = 6; // OK
Constant pointers
Once a constant pointer points to a variable then it cannot point to any other variable.
int var1 = 1;
int var2 = 0;
int *const ptr2 = &var1;
ptr2 = &var2; // Error
Pointer to constant
A pointer through which one cannot change the value of a variable it points is known as a pointer to constant.
int const * ptr3 = &var2;
*ptr3 = 4; // Error
Constant pointer to a constant
A constant pointer to a constant is a pointer that can neither change the address it's pointing to and nor can it change the value kept at that address.
int var3 = 0;
int var4 = 0;
const int * const ptr4 = &var3;
*ptr4 = 1; // Error
ptr4 = &var4; // Error
This question shows precisely why I like to do things the way I mentioned in my question is const after type id acceptable?
In short, I find the easiest way to remember the rule is that the "const" goes after the thing it applies to. So in your question, "int const *" means that the int is constant, while "int * const" would mean that the pointer is constant.
If someone decides to put it at the very front (eg: "const int *"), as a special exception in that case it applies to the thing after it.
Many people like to use that special exception because they think it looks nicer. I dislike it, because it is an exception, and thus confuses things.
The general rule is that the const keyword applies to what precedes it immediately. Exception, a starting const applies to what follows.
const int* is the same as int const* and means "pointer to constant int".
const int* const is the same as int 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?
Simple Use of const.
The simplest use is to declare a named constant. To do this, one declares a constant as if it was a variable but add const before it. One has to initialize it immediately in the constructor because, of course, one cannot set the value later as that would be altering it. For example:
const int Constant1=96;
will create an integer constant, unimaginatively called Constant1, with the value 96.
Such constants are useful for parameters which are used in the program but are do not need to be changed after the program is compiled. It has an advantage for programmers over the C preprocessor #define command in that it is understood & used by the compiler itself, not just substituted into the program text by the preprocessor before reaching the main compiler, so error messages are much more helpful.
It also works with pointers but one has to be careful where const to determine whether the pointer or what it points to is constant or both. For example:
const int * Constant2
declares that Constant2 is variable pointer to a constant integer and:
int const * Constant2
is an alternative syntax which does the same, whereas
int * const Constant3
declares that Constant3 is constant pointer to a variable integer and
int const * const Constant4
declares that Constant4 is constant pointer to a constant integer. Basically ‘const’ applies to whatever is on its immediate left (other than if there is nothing there in which case it applies to whatever is its immediate right).
ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
It's simple but tricky. Please note that we can apply the const qualifier to 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 pointer p 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-only
const int *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p 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 to int const *p
int const *const p ==> *p and p are read-only [p is a constant pointer to a constant integer]. As pointer p here is read-only, the declaration and definition should be in same place.
I had the same doubt as you until I came across this book by the C++ Guru Scott Meyers. Refer the third Item in this book where he talks in details about using const.
Just follow this advice
If the word const appears to the left of the asterisk, what's pointed to is constant
If the word const appears to the right of the asterisk, the pointer itself is constant
If const appears on both sides, both are constant
To remember in easy way :
If const is before * then value is constant.
If const is after * then address is constant.
if const are available both before and after * then both value and address are constant.
e.g.
int * const var; //here address is constant.
int const * var; //here value is constant.
int const * const var; // both value and address are constant.
The C and C++ declaration syntax has repeatedly been described as a failed experiment, by the original designers.
Instead, let's name the type “pointer to Type”; I’ll call it Ptr_:
template< class Type >
using Ptr_ = Type*;
Now Ptr_<char> is a pointer to char.
Ptr_<const char> is a pointer to const char.
And const Ptr_<const char> is a const pointer to const char.
For me, the position of const i.e. whether it appears to the LEFT or RIGHT or on both LEFT and RIGHT relative to the * helps me figure out the actual meaning.
A const to the LEFT of * indicates that the object pointed by the pointer is a const object.
A const to the RIGHT of * indicates that the pointer is a const pointer.
The following table is taken from Stanford CS106L Standard C++ Programming Laboratory Course Reader.
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 a TYPE 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 drew an image below to explain this, maybe helpful.
int const v and const int v are identical.
The const with the int on either sides will make pointer to constant int:
const int *ptr=&i;
or:
int const *ptr=&i;
const after * will make constant pointer to int:
int *const ptr=&i;
In this case all of these are pointer to constant integer, but none of these are constant pointer:
const int *ptr1=&i, *ptr2=&j;
In this case all are pointer to constant integer and ptr2 is constant pointer to constant integer. But ptr1 is not constant pointer:
int const *ptr1=&i, *const ptr2=&j;
if const is to the left of *, it refers to the value (it doesn't matter whether it's const int or int const)
if const is to the right of *, it refers to the pointer itself
it can be both at the same time
An important point: const int *p does not mean the value you are referring to is constant!!. It means that you can't change it through that pointer (meaning, you can't assign $*p = ...`). The value itself may be changed in other ways. Eg
int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error
This is meant to be used mostly in function signatures, to guarantee that the function can't accidentally change the arguments passed.
This mostly addresses the second line: best practices, assignments, function parameters etc.
General practice. Try to make everything const that you can. Or to put that another way, make everything const to begin with, and then remove exactly the minimum set of consts necessary to allow the program to function. This will be a big help in attaining const-correctness, and will help ensure that subtle bugs don't get introduced when people try and assign into things they're not supposed to modify.
Avoid const_cast<> like the plague. There are one or two legitimate use cases for it, but they are very few and far between. If you're trying to change a const object, you'll do a lot better to find whoever declared it const in the first pace and talk the matter over with them to reach a consensus as to what should happen.
Which leads very neatly into assignments. You can assign into something only if it is non-const. If you want to assign into something that is const, see above. Remember that in the declarations int const *foo; and int * const bar; different things are const - other answers here have covered that issue admirably, so I won't go into it.
Function parameters:
Pass by value: e.g. void func(int param) you don't care one way or the other at the calling site. The argument can be made that there are use cases for declaring the function as void func(int const param) but that has no effect on the caller, only on the function itself, in that whatever value is passed cannot be changed by the function during the call.
Pass by reference: e.g. void func(int ¶m) Now it does make a difference. As just declared func is allowed to change param, and any calling site should be ready to deal with the consequences. Changing the declaration to void func(int const ¶m) changes the contract, and guarantees that func can now not change param, meaning what is passed in is what will come back out. As other have noted this is very useful for cheaply passing a large object that you don't want to change. Passing a reference is a lot cheaper than passing a large object by value.
Pass by pointer: e.g. void func(int *param) and void func(int const *param) These two are pretty much synonymous with their reference counterparts, with the caveat that the called function now needs to check for nullptr unless some other contractual guarantee assures func that it will never receive a nullptr in param.
Opinion piece on that topic. Proving correctness in a case like this is hellishly difficult, it's just too damn easy to make a mistake. So don't take chances, and always check pointer parameters for nullptr. You will save yourself pain and suffering and hard to find bugs in the long term. And as for the cost of the check, it's dirt cheap, and in cases where the static analysis built into the compiler can manage it, the optimizer will elide it anyway. Turn on Link Time Code Generation for MSVC, or WOPR (I think) for GCC, and you'll get it program wide, i.e. even in function calls that cross a source code module boundary.
At the end of the day all of the above makes a very solid case to always prefer references to pointers. They're just safer all round.
Just for the sake of completeness for C following the others explanations, not sure for C++.
pp - pointer to pointer
p - pointer
data - the thing pointed, in examples x
bold - read-only variable
Pointer
p data - int *p;
p data - int const *p;
p data - int * const p;
p data - int const * const p;
Pointer to pointer
pp p data - int **pp;
pp p data - int ** const pp;
pp p data - int * const *pp;
pp p data - int const **pp;
pp p data - int * const * const pp;
pp p data - int const ** const pp;
pp p data - int const * const *pp;
pp p data - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);
// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);
N-levels of Dereference
Just keep going, but may the humanity excommunicate you.
int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;
printf("%d \n", ****pppp);
const int* - pointer to constant int object.
You can change the value of the pointer; you can not change the value of the int object, the pointer points to.
const int * const - constant pointer to constant int object.
You can not change the value of the pointer nor the value of the int object the pointer points to.
int const * - pointer to constant int object.
This statement is equivalent to 1. const int* - You can change the value of the pointer but you can not change the value of the int object, the pointer points to.
Actually, there is a 4th option:
int * const - constant pointer to int object.
You can change the value of the object the pointer points to but you can not change the value of the pointer itself. The pointer will always point to the same int object but this value of this int object can be changed.
If you want to determine a certain type of C or C++ construct you can use the Clockwise/Spiral Rule made by David Anderson; but not to confuse with Anderson`s Rule made by Ross J. Anderson, which is something quite distinct.
simple mnemonic:
type pointer <- * -> pointee name
I like to think of int *i as declaring "the dereference of i is int"; in this sense, const int *i means "the deref of i is const int", while int *const i means "deref of const i is int".
(the one danger of thinking like this is it may lead to favoring int const *i style of declaration, which people might hate/disallow)
Nobody has mentioned the system underlying declarations which Kernighan and Ritchie pointed out in their C book:
Declarations mimic expressions.
I'll repeat this because it so essential and gives a clear strategy to parse even the most complicated declarations:
Declarations mimic expressions.
The declarations contain the same operators as expressions the declared identifier can appear in later, with the same priority they have in expressions. This is why the "clockwise spiral rule" is wrong: The evaluation order is strictly determined by the operator precedences, with complete disregard for left, right or rotational directions.
Here are a few Examples, in order of increasing complexity:
int i;: When i is used as-is, it is an expression of type int. Therefore, i is an int.
int *p;: When p is dereferenced with *, the expression is of type int. Therefore, p is a pointer to int.
const int *p;: When p is dereferenced with *, the expression is of type const int. Therefore, p is a pointer to const int.
int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type int. Therefore, p is a const pointer to int.
const int *const p;: p is const. If this constant expression is dereferenced with *, the expression is of type const int. Therefore, p is a const pointer to const int.
So far we didn't have any issues with operator precedence yet: We simply evaluated right-to-left. This changes when we have fun with arrays of pointers and pointers to arrays. You may want to have a cheat sheet open.
int a[3];: When we apply the array indexing operator to a, the result is an int. Therefore, a is an array of int.
int *a[3];: Here the indexing operator has higher precedence, so we apply it first: When we apply the array indexing operator to a, the result is an int *. Therefore, a is an array of pointers to int. This is not uncommon.
int (*a)[3];: Here the operator precedence is overridden by round parentheses, exactly as in any expression. Consequently, we dereference first. We know now that a is a pointer to some type. *a, the dereferenced pointer, is an expression of that type. When we apply the array indexing operator to *a, we obtain a plain int, which means that *a is an array of three ints, and a is a pointer to that array. This is fairly uncommon outside of C++ templates, which is why the operator precedences are not catering to this case. Note how the use of such a pointer is the model for its declaration: int i = (*a)[1];. The parentheses are mandatory to dereference first.
int (*a)[3][2];: There is nothing preventing anybody from having pointers to multi-dimensional arrays, one case where circular spiral clockwise advice becomes obvious nonsense.
A thing that sometimes comes up in real life are function pointers. We need parentheses there as well because the function call operator (operator()() in C++, simple syntax rule in C) has higher priority than the dereferencing operator*(), again because it's more common to have functions returning pointers than pointers to functions:
int *f();: Function call first, so f is a function. The call must be dereferenced to result in an int, so the return value is a pointer to int. Usage: int i = *f();.
int (*fp)();: Parentheses change order of operator application. Because we must dereference first we know that fp is a pointer to something. Because we can apply the function call operator to *fp we know (in C) that fp is a pointer to a function; in C++ we only know that it is something for which operator()() is defined. Since the call takes no parameters and returns an int, fp is in C++ a pointer to a function with that signature. (In C, an empty parameter list indicates that nothing is known about the parameters, but future C specifications may forbid that obsolete use.)
int *(*fp)();: Of course we can return pointers to int from a function pointed to.
int (*(*fp)())[3];: Dereference first, hence a pointer; apply function call operator next, hence a pointer to function; dereference the return value again, hence a pointer to a function returning a pointer; apply the indexing operator to that: pointer to function returning pointer to array. The result is an int, hence pointer to function returning pointer to array of ints.-
All parentheses are necessary: As discussed, we must prioritize dereferencing of the function pointer with (*fp) before anything else happens. Obviously, we need the function call; and since the function returns a pointer to an array (not to its first element!), we must dereference that as well before we can index it. I admit that I wrote a test program to check this because I wasn't sure, even with this fool-proof method ;-). Here it is:
#include <iostream>
using namespace std;
int (*f())[3]
{
static int arr[3] = {1,2,3};
return &arr;
}
int (*(*fp)())[3] = &f;
int main()
{
for(int i=0; i<3; i++)
{
cout << (*(*fp)())[i] << endl;
}
}
Note how beautifully the declaration mimics the expression!
Lot of people answered correctly I will just organize well here and put some Extra info which is missing in given Answers.
Const is keyword in C language also known as qualifier. Const can
applied to the declaration of any variable to specify that it's value
will not changed
const int a=3,b;
a=4; // give error
b=5; // give error as b is also const int
you have to intialize while declaring itself as no way to assign
it afterwards.
How to read ?
just read from right to left every statement works smoothly
3 main things
type a. p is ptr to const int
type b. p is const ptr to int
type c. p is const ptr to const int
[Error]
if * comes before int
two types
1. const int *
2. const const int *
we look first
Major type 1. const int*
ways to arrange 3 things at 3 places 3!=6
i. * at start
*const int p [Error]
*int const p [Error]
ii. const at start
const int *p type a. p is ptr to const int
const *int p [Error]
iii. int at start
int const *p type a.
int * const p type b. p is const ptr to int
Major type 2. const const int*
ways to arrange 4 things at 4 places in which 2 are alike 4!/2!=12
i. * at start
* int const const p [Error]
* const int const p [Error]
* const const int p [Error]
ii. int at start
int const const *p type a. p is ptr to const int
int const * const p type c. p is const ptr to const int
int * const const p type b. p is const ptr to int
iii. const at start
const const int *p type a.
const const * int p [Error]
const int const *p type a.
const int * const p type c.
const * int const p [Error]
const * const int p [Error]
squeezing all in one
type a. p is ptr to const int (5)
const int *p
int const *p
int const const *p
const const int *p
const int const *p
type b. p is const ptr to int (2)
int * const p
int * const const p;
type c. p is const ptr to const int (2)
int const * const p
const int * const p
just little calculation
1. const int * p total arrangemets (6) [Errors] (3)
2. const const int * p total arrangemets (12) [Errors] (6)
little Extra
int const * p,p2 ;
here p is ptr to const int (type a.)
but p2 is just const int please note that it is not ptr
int * const p,p2 ;
similarly
here p is const ptr to int (type b.)
but p2 is just int not even cost int
int const * const p,p2 ;
here p is const ptr to const int (type c.)
but p2 is just const int.
Finished