What's the point of const pointers?

C++CPointersConstants

C++ Problem Overview


I'm not talking about pointers to const values, but const pointers themselves.

I'm learning C and C++ beyond the very basic stuff and just until today I realized that pointers are passed by value to functions, which makes sense. This means that inside a function I can make the copied pointer point to some other value without affecting the original pointer from the caller.

So what's the point of having a function header that says:

void foo(int* const ptr);

Inside such a function you cannot make ptr point to something else because it's const and you don't want it to be modified, but a function like this:

void foo(int* ptr);

Does the work just as well! because the pointer is copied anyways and the pointer in the caller is not affected even if you modify the copy. So what's the advantage of const?

C++ Solutions


Solution 1 - C++

const is a tool which you should use in pursuit of a very important C++ concept:

> Find bugs at compile-time, rather than run-time, by getting the compiler to enforce what you mean.

Even though it doesn't change the functionality, adding const generates a compiler error when you're doing things you didn't mean to do. Imagine the following typo:

void foo(int* ptr)
{
	ptr = 0;// oops, I meant *ptr = 0
}

If you use int* const, this would generate a compiler error because you're changing the value to ptr. Adding restrictions via syntax is a good thing in general. Just don't take it too far -- the example you gave is a case where most people don't bother using const.

Solution 2 - C++

I make a point of using only const arguments because this enables more compiler checks: if I accidentally re-assign an argument value inside the function, the compiler bites me.

I rarely reuse variables, it’s cleaner to create new variables to hold new values, so essentially all my variable declarations are const (except for some cases such as loop variables where const would prevent the code from working).

Note that this makes only sense in the definition of a function. It doesn’t belong in the declaration, which is what the user sees. And the user doesn’t care whether I use const for parameters inside the function.

Example:

// foo.h
int frob(int x);

// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

Notice how both the argument and the local variable are const. Neither is necessary but with functions that are even slightly larger, this has repeatedly saved me from making mistakes.

Solution 3 - C++

Your question touches on something more general: Should function arguments be const?

The constness of value arguments (like your pointer) is an implementation detail, and it does not form part of the function declaration. This means that your function is always this:

void foo(T);

It is entirely up to the implementer of the function whether she wants to use the functions-scope argument variable in a mutable or in a constant way:

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

So, follow the simple rule to never put const in the declaration (header), and put it in the definition (implementation) if you don't want or need to modify the variable.

Solution 4 - C++

The top level const qualifier is discarded in declarations, so the declarations in the question declare exactly the same function. On the other hand, in the definition (implementation) the compiler will verify that if you mark the pointer as const, it is not modified inside the body of the function.

Solution 5 - C++

There is a lot to the const keyword, it is a rather complex one. Generally, adding a lot of const to your program is considered good programming practice, search the web for "const correctness" and you'll find plenty of info about that.

The const keyword is a so-called "type qualifier", others are volatile and restrict. At least volatile follows the same (confusing) rules as const.


First of all, the const keyword serves two purposes. The most obvious one is to protect data (and pointers) from intentional or accidental misuse by making them read-only. Any attempt to modify a const variable will be spotted by the compiler at compile-time.

But there is also another purpose in any system with read-only memory, namely to ensure that a certain variable is allocated inside such memory - it could be EEPROM or flash for example. These are known as non-volatile memories, NVM. A variable allocated in NVM will still of course follow all the rules of a const variable.

There are several different ways to use the const keyword:

Declare a constant variable.

This can be done either as

const int X=1; or
int const X=1;

These two forms are completely equivalent. The latter style is considered bad style and should not be used.

The reason why the second row is considered bad style, is probably because "storage-class specifiers" such as static and extern also can be declared after the actual type, int static etc. But doing so for storage-class specifiers is labelled as an obsolete feature by the C committee (ISO 9899 N1539 draft, 6.11.5). Therefore, for the sake of consistency one should not write type qualifiers in that manner either. It serves no other purpose but to confuse the reader anyhow.

Declare a pointer to a constant variable.

const int* ptr = &X;

This means that the contents of 'X' cannot be modified. This is the normal way you declare pointers like this, mainly as part of function parameters for "const correctness". Because 'X' doesn't actually have to be declared as const, it could be any variable. In other words you can always "upgrade" a variable to const. Technically, C also allows downgrading from const to a plain variable by explicit typecasts, but doing so is considered bad programming and compilers usually give warnings against it.

Declare a constant pointer

int* const ptr = &X;

This means that the pointer itself is constant. You can modify what it points at, but you cannot modify the pointer itself. This doesn't have many uses, there are a few, like ensuring that a pointer-pointed-at (pointer-to-pointer) doesn't have it's address changed while passed as parameter to a function. You'll have to write something not-too-readable like this:

void func (int*const* ptrptr)

I doubt many C programmers can get the const and * right in there. I know I can't - I had to check with GCC. I think that's why you rarely ever see that syntax for pointer-to-pointer, even though it is considered good programming practice.

Constant pointers can also be used to ensure that the pointer variable itself is declared in read-only memory, for example you could want to declare some sort of pointer-based lookup table and allocate it in NVM.

And of course, as indicated by other answers, constant pointers can also be used to enforce "const correctness".

Declare a constant pointer to constant data

const int* const ptr=&X;

This is the two pointer types described above combined, with all attributes of them both.

Declare a read-only member function (C++)

Since this is tagged C++, I should also mention that you can declare member functions of a class as const. This means that the function isn't allowed to modify any other member of the class when it is called, which both prevents the programmer of the class from accidental errors but also informs the caller of the member function that they won't be messing anything up by calling it. The syntax is:

void MyClass::func (void) const;

Solution 6 - C++

You're right, for the caller it makes absolutely no difference. But for the writer of the function it can be a safety net "okay, I need to make sure I don't make this point to the wrong thing". Not very useful but not useless either.

It's basically the same as having an int const the_answer = 42 in your program.

Solution 7 - C++

> ...today I realized that pointers are passed by value to functions, which makes sense.

(imo) it really doesn't make sense as the default. the more sensible default is to pass as non-reassignable pointer (int* const arg). that is, i would have preferred that pointers passed as arguments were implicitly declared const.

> So what's the advantage of const?

the advantage is that it's easy enough and sometimes unclear when you do modify the address the argument points to, such that you can introduce a bug when it is not const rather easily. altering the address is atypical. it's clearer to create a local variable if your intent is to modify the address. as well, raw pointer manipulation is an easy way to introduce bugs.

so it's clearer to pass by immutable address and create a copy (in those atypical cases) when you want to alter the address the argument points to:

void func(int* const arg) {
	int* a(arg);
	...
	*a++ = value;
}

adding that local is virtually free, and it reduces the chance for errors, while improving readability.

at a higher level: if you are manipulating the argument as an array, it's typically clearer and less error prone to the client to declare the argument as a container/collection.

in general, adding const to values, arguments, and addresses is a good idea because you don't always realize the side effects, which the compiler happily enforces. therefore, it's as useful as const as used in other several other cases (e.g. the question is similar to 'Why should I declare values const?'). fortunately, we also have references, which cannot be reassigned.

Solution 8 - C++

If you do embedded systems or device driver programming where you have memory mapped devices then both forms of 'const' are often used, one to prevent the pointer from being reassigned (since it points to a fixed hardware address.) and, if the peripheral register it points to is a read-only hardware register then another const will detect a lot of errors at compile time rather than runtime.

A read-only 16 bit peripheral chip register might look something like:

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

Then you can easily read the hardware register without having to resort to assembly language:

input_word = *peripheral;

Solution 9 - C++

int iVal = 10; int *const ipPtr = &iVal;

Just like a normal const variable, a const pointer must be initialized to a value upon declaration, and its value can not be changed.

This means a const pointer will always point to the same value. In the above case, ipPtr will always point to the address of iVal. However, because the value being pointed to is still non-const, it is possible to change the value being pointed to via dereferencing the pointer:

*ipPtr = 6; // allowed, since pnPtr points to a non-const int

Solution 10 - C++

Same question can be asked about any other type (not just pointers):

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}

Solution 11 - C++

Your question is really more about why define any variable as a const not just const pointer parameter to a function. The same rules apply here as when you define any variable as constant, if its a parameter to function or member variable or a local variable.

In your particular case, functionally it doesn't make difference like in many other cases when you declare a local variable as const but it does put a restriction that you can't modify this variable.

Solution 12 - C++

Passing a const pointer to a function makes little sense, as it will be passed by value anyways. It's just one of those things that are allowed by the general language design. Prohibiting it just because it doesn't make sense would just make the language spec. larger.

If you are inside a function it is of course another case. Having a pointer that cannot change what it points to is an assertion that makes the code clearer.

Solution 13 - C++

I guess an advantage would be that the compiler can perform more aggressive optimizations inside the function knowing that this pointer cannot change.

It also avoids eg. passing this pointer to a subfunction which accepts a non-const pointer reference (and could therefore change the pointer like void f(int *&p)), but I agree, that the usefulness is somewhat limited in this case.

Solution 14 - C++

An example of where a const pointer is highly applicable can be demonstrated thusly. Consider you have a class with a dynamic array inside it, and you want to pass the user access to the array but without granting them the rights to change the pointer. Consider:

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

Which produces:

> Input data
> put data

But if we try this:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

We get:

> error: lvalue required as left operand of assignment //Drat foiled again!

So clearly we can modify the array's contents, but not the array's pointer. Good if you want to make sure the pointer has a consistent state when passing it back to the user. There is one catch, though:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

We can still delete the pointer's memory reference, even if we can't modify the pointer itself.

So if you want the memory reference to always point to something (IE never be modified, similar to how a reference currently works), then it's highly applicable. If you want the user to have full access and modify it, then non-const is for you.

Edit:

After noting okorz001 comment of not being able to assign due to GetArray() being a right-value operand, his comment is entirely correct, but the above still applies if you were to return a reference to the pointer (I suppose I assumed GetArray was referring a reference), for example:

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

Will return in the first resulting in an error:

> error: assignment of read-only location 'Temp.TestA::GetArray()'

But the second will occur merrily despite potential consequences on the underneath.

Obviously, the question will be raised 'why would you want to return a reference to a pointer'? There are rare instances where you need to assign memory (or data) directly to the original pointer in question (for example, building your own malloc/free or new/free front-end), but in those instances it's a non-const reference. A reference to a const pointer I've not come across a situation that would warrant it (unless maybe as declared const reference variables rather than return types?).

Consider if we have a function that takes a const pointer (versus one that doesn't):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

The error in the const produces thus message:

> error: decrement of read-only parameter 'Data'

Which is good as we probably don't want to do that, unless we want to cause the problems denoted in the comments. If we edit out the decrement in the const function, the following occurs:

> NonConst: A
> Const: B

Clearly, even though A is 'Data[1]', it's being treated as 'Data[0]' because the NonConst pointer permitted the decrement operation. With the const implemented, as another person writes, we catch the potential bug before it occurs.

One other main consideration, is that a const pointer can be used as a pseudo reference, in that the thing the reference points to cannot be changed (one wonders, if perhaps this was how it was implemented). Consider:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

When attempting to compile, produces the following error:

> error: assignment of read-only variable 'B'

Which is probably a bad thing if a constant reference to A was wanted. If B = NULL is commented out, the compiler will happily let us modify *B and therefore A. This might not seem useful with ints, but consider if you had a single stance of a graphical application where you wanted an unmodifiable pointer that referred to it that you could pass around.

It's usage is variable (excuse the unintended pun), but used correctly, it is another tool in the box to assist with programming.

Solution 15 - C++

There's nothing special about pointers where you would never want them to be const. Just as you can have class member constant int values, you can also have constant pointers for similar reasons: You want to make sure that no one ever changes what's being pointed to. C++ references somewhat address this, but the pointer behavior is inherited from C.

Solution 16 - C++

I believe this would prevent code from incrementing or decrementing the pointer within the function body.

Solution 17 - C++

Types of declaring any variables like-
(1)Declaring a constant variable.
DataType const varibleName;

 int const x;
x=4; //you can assign its value only One time
(2)Declare a pointer to a constant variable
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
//Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
//Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionR. Ruiz.View Question on Stackoverflow
Solution 1 - C++tenfourView Answer on Stackoverflow
Solution 2 - C++Konrad RudolphView Answer on Stackoverflow
Solution 3 - C++Kerrek SBView Answer on Stackoverflow
Solution 4 - C++David Rodríguez - dribeasView Answer on Stackoverflow
Solution 5 - C++LundinView Answer on Stackoverflow
Solution 6 - C++cnicutarView Answer on Stackoverflow
Solution 7 - C++justinView Answer on Stackoverflow
Solution 8 - C++Dan HaynesView Answer on Stackoverflow
Solution 9 - C++jusathrView Answer on Stackoverflow
Solution 10 - C++pmgView Answer on Stackoverflow
Solution 11 - C++zarView Answer on Stackoverflow
Solution 12 - C++Anders AbelView Answer on Stackoverflow
Solution 13 - C++MartinStettnerView Answer on Stackoverflow
Solution 14 - C++SE Does Not Like DissentView Answer on Stackoverflow
Solution 15 - C++Mark BView Answer on Stackoverflow
Solution 16 - C++Mike ChristensenView Answer on Stackoverflow
Solution 17 - C++PyadavView Answer on Stackoverflow