What's the point of const pointers?

33,807

Solution 1

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

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

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

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

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;
Share:
33,807

Related videos on Youtube

R. Ruiz.
Author by

R. Ruiz.

Updated on July 08, 2022

Comments

  • R. Ruiz.
    R. Ruiz. almost 2 years

    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?

    • Platinum Azure
      Platinum Azure over 12 years
      What if you want to guarantee, at compile time, that the pointer cannot and should not be modified to point to something else?
    • David Heffernan
      David Heffernan over 12 years
      Exactly the same point as any const parameter.
    • R. Ruiz.
      R. Ruiz. over 12 years
      @PlatinumAzure, I have a few years of experience programming, yet my software practices are lacking. I'm glad I made this question because I have never felt the need to make the compiler point out my own logic errors. My initial reaction at the moment I submitted the question was "why should I care if the pointer is modified?, it won't affect the caller". Until I read all of the answers I understood that I should care because if I try to modify it, me and my coding logic are wrong (or there was a typo like missing the asterisk) and I might have not realized if the compiler didn't tell me.
    • Platinum Azure
      Platinum Azure over 12 years
      @R.Ruiz. Doubtless, even the most experienced of us could do with extra guarantees of correctness. Because software engineering is a form of engineering where a little more leeway may be acceptable (random HTTP 502s, slow connections, the occasional image failing to load are not extraordinary occasions, but an engine failing in an airplane is unacceptable and likely severe), sometimes people program with undue haste. The same argument that justifies writing unit tests will explain the use of const-correctness guarantees. It just makes us feel more sure that our code is indubitably correct.
    • Raedwald
      Raedwald over 12 years
    • underscore_d
      underscore_d almost 7 years
      @Raedwald The questions look like exact duplicates to me. There are more good answers on this one, but they all became more general than asked, by talking about const on arguments overall, not just pointers.
  • Lundin
    Lundin over 12 years
    What does it matter where they point it, it is a pointer allocated on the stack? The function can't mess that up. It makes far more sense to use read-only pointers when dealing with pointer-to-pointers. It is not the same thing as int const! I'll downvote this just for that misleading statement. const int and int const are equivalent, while const int*and int* const have two different meanings!
  • cnicutar
    cnicutar over 12 years
    @lundin Let's suppose the function is big and has other stuff in it. Accidentally one may make it point to something else (on the stack of the function). This isn't problematic for the caller but certainly could be for the callee. I don't see anything misleading in my statements: "for the writer of the function it can be a safety net".
  • cnicutar
    cnicutar over 12 years
    @Lundin About int const part; I have been putting the type before const (it does sound unnatural) for some time and I am aware of the differences. This article might prove useful. I myself had slightly different reasons to switch to this style however.
  • sbi
    sbi over 12 years
    +1 from another const-obsessed fanatic. However, I prefer my compilers to bark at me. I make too many errors and would suffer to badly would they bite.
  • Konrad Rudolph
    Konrad Rudolph over 12 years
    +1 for this being the default. C++, like most languages, has it the wrong way round. Instead of having a const keyword, it should have a mutable keyword (well, it has, but with the wrong semantics).
  • Lundin
    Lundin over 12 years
    Okay. I just wrote a long-winded answer of my own in case someone else but you and me are reading this post. I also included a rationale why int const is bad style and const int is good style.
  • Oscar Korz
    Oscar Korz over 12 years
    Temp.GetArray() = NULL fails because Temp.GetArray() is an rvalue, not because it is const-qualified. Additionally, I believe the const-qualifier is stripped from all return types.
  • R. Ruiz.
    R. Ruiz. over 12 years
    Thank you, this is an answer that convinces me. You put const so that the compiler warns you about your own assignment errors. Your example is perfect to illustrate this concept because that is a common mistake with pointers. Than you!
  • R. Ruiz.
    R. Ruiz. over 12 years
    Lundin, I'm also reading! I agree const int is the better style though. @cnicutar, your first response to Lundin is what I was looking for when I submitted this question. So the problem is not in the caller (as I mindlessly thought), but const is an assurance for the callee. I like this idea, thank you.
  • R. Ruiz.
    R. Ruiz. over 12 years
    I didn't know that. So if I try to overload void foo(int* const ptr) with void foo(int* t ptr) I will get a compiler error. Thanks!
  • R. Ruiz.
    R. Ruiz. over 12 years
    Interesting idea having const as the default. Thanks for your answer!
  • R. Ruiz.
    R. Ruiz. over 12 years
    +1 for the optimizations. At least the books say it's true. What I would like is to understand exactly what those optimizations are
  • R. Ruiz.
    R. Ruiz. over 12 years
    your comment makes realize how futile is my question because you are right: it is the same thing as having any other parameter as const. It may seem useless but it's there to help the programmer have this restriction and avoid bugs
  • R. Ruiz.
    R. Ruiz. over 12 years
    LOL, you are right. The case of pointers came to my mind first because I thought they were special, but they are just like any other variable passed by value.
  • Flexo
    Flexo over 12 years
    "Help the compiler help you" is the mantra I normally chant for this.
  • Flexo
    Flexo over 12 years
    +1, I highly recommend the "const unless there's a good reason" strategy. There are some good exceptions though, e.g. Copy and swap
  • Raedwald
    Raedwald over 12 years
    +1, but here is an interesting case where it is argueable: stackoverflow.com/questions/6305906/…
  • Keith Thompson
    Keith Thompson over 12 years
    I agree with this -- but I'm somewhat uncomfortable with the idea of making the declaration and definition different. For things other than const, the declaration and the (prototype part of) the definition are generally going to be identical.
  • Keith Thompson
    Keith Thompson over 12 years
    If I were designing a new language, declared objects would be read-only ("const") by default. You'd need some special syntax, perhaps a "var" keyword, to make an object writable.
  • Lundin
    Lundin over 12 years
    Type qualifiers should be written in the same manner as storage class specifiers, or your coding style is illogical and inconsistent. Do you also write int extern, int static? I bet you don't, and you shouldn't, since this style is also going to be removed from the C language (see my answer to the OP for details).
  • Pyadav
    Pyadav over 12 years
    We provide a pointer variable as a const into a function as an argument when we don't wana to change its actual value
  • Konrad Rudolph
    Konrad Rudolph over 12 years
    @Keith I’m tempted to say “of course”. Everything else is stupid at this point. But unfortunately most language designers seem to disagree with us … In fact, I’ve already posted as much (in a now deleted question, “What’s your most controversial programming opinion?”).
  • Kerrek SB
    Kerrek SB over 12 years
    @KeithThompson: Well, you don't have to do it if you don't want to. Just don't put const in the declaration. It's entirely up to you if you want to add the qualifier in the implementation.
  • Lundin
    Lundin over 12 years
    @cnicutar No, Saks tends to write only nonsense and that article is no exception. Does typedef char* ntcs; const ntcs s; give a const pointer or pointer to const? The answer: muppets hiding away their pointers inside typedefs deserve all the bugs they get.
  • SE Does Not Like Dissent
    SE Does Not Like Dissent over 12 years
    @okorz001: After testing, you are indeed correct. However, the above applies if you return a reference to the pointer itself. I will edit my post accordingly.
  • Keith Thompson
    Keith Thompson over 12 years
    As I said, I agree that putting const on the declaration but not the definition makes sense. It just feels like a glitch in the language that this is the only case where making the declaration and definition non-identical makes sense. (It's hardly C's only glitch, of course.)
  • Lee Louviere
    Lee Louviere over 12 years
    so it's the same answer you'd give to "Why make variables private, when you can just not use them outside the class."
  • Clément
    Clément over 9 years
    The "constant unless noted otherwise" approach is common in most functional languages (let a = ... in ...), where you need to explicitly create "references" to be allowed to modify variables (let a = ref ... in ...).
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica almost 9 years
    In C++11, if you need an internal copy of a function parameter, then having the copy done while the parameter is passed by value exposes additional optimization possibilities. When you need a copy, passing const values as arguments is a premature pessimization in C++11.
  • Konrad Rudolph
    Konrad Rudolph almost 9 years
    @KubaOber That isn't specific to C++11, the same was true to a lesser extent before.
  • Trevor Hickey
    Trevor Hickey over 8 years
    @R.Ruiz. If your deceleration is void foo(int* t ptr) and your definition is void foo(int* const ptr), you will NOT get a compiler error.
  • underscore_d
    underscore_d almost 7 years
    @KubaOber I don't see how it's a pessimisation at all. If you later find that you need to modify what was passed within the body of the function, then just drop the const from the argument, and preferably comment why. That tiny extra bit of work doesn't justify marking all arguments as non-const by default and opening yourself up to all the potential errors that creates.
  • underscore_d
    underscore_d almost 7 years
    @TrevorHickey They will... but not for the reason they think. int* t ptr is a syntax error. Without that, the two are identical for overloading purposes.
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica almost 7 years
    Of course you can change the signature if you are allowed to change signatures, and if you have some sort of a process in place to ensure that such changes don't cause regressions.
  • Konrad Rudolph
    Konrad Rudolph almost 7 years
    @KubaOber constness of an argument isn't part of the public API, it's an implementation detail. You can change it without even recompiling the client code. Reread the answer.
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica almost 7 years
    Any discussion of whether constness of an argument is part of the API or not is impossible without selecting an ABI, because the API is-the ABI in this discussion. From the point of view of C++ standard, any such signature change without recompilation is undefined behavior, so you'd have to argue that a certain ABI allows it. It's brittle at the very least.
  • Konrad Rudolph
    Konrad Rudolph almost 7 years
    @KubaOber You're wrong, this is specified by the language, and it's not a change in signature. Reread the answer.
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica almost 7 years
    I was arguing from the point of the answer being wrong. Alas it's not. Now that I've dug into the standard - good to learn that I was wrong. Cool stuff, easy to miss unless you know what you're looking for. Alas, it unfortunately exposes another can of worms: for values that are not cheap to copy, the implementation will leak as you switch from const T & to T and those definitely are never ABI compatible :(
  • Tomer
    Tomer over 6 years
    this might be a little off topic, but where does this const pointer sit in the memory? I know all const sit in the global part of the memory, but I am not 100% sure about a const that you get passed in as a func var. thanks and sorry if this is off topic
  • Porcupine
    Porcupine about 6 years
    Excellent point: the user doesn’t care whether I use const for parameters inside the function.
  • ysap
    ysap over 3 years
    @tomer - A const variable can be local. I think you are confusing with static. A static variable, cannot, by its very nature be allocated in the stack. (That comment is a few years old now, though, you may already know that)