132

I'm running through some example programs to refamiliarize myself with C++ and I have run into the following question. First, here is the example code:

void print_string(const char * the_string)
{
    cout << the_string << endl;
}

int main () {
    print_string("What's up?");
}

In the above code, the parameter to print_string could have instead been const char * const the_string. Which would be more correct for this?

I understand that the difference is that one is a pointer to a constant character, while the other one is a constant pointer to a constant character. But why do both of these work? When would it be relevant?

malat
  • 12,152
  • 13
  • 89
  • 158
pict
  • 1,373
  • 3
  • 9
  • 5

12 Answers12

292

The latter prevents you from modifying the_string inside print_string. It would actually be appropriate here, but perhaps the verbosity put off the developer.

char* the_string : I can change which char the_string points to, and I can modify the char to which it points.

const char* the_string : I can change which char the_string points to, but I cannot modify the char to which it points.

char* const the_string : I cannot change which char the_string points to, but I can modify the char to which it points.

const char* const the_string : I cannot change which char the_string points to, nor can I modify the char to which it points.

jazzpi
  • 1,399
  • 12
  • 18
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • 13
    +1 for the last sentence. Const-correctness is verbose, but well worth it. – mskfisher Feb 09 '11 at 19:08
  • 5
    @mskfisher: One reason I force myself to write `char const*`. – Xeo Feb 09 '11 at 19:24
  • 6
    @Xeo: your form is even more confusing because it's one transposition away from changing its meaning entirely. `const char *` is much better because the `const` is on the complete opposite side. – R.. GitHub STOP HELPING ICE Feb 09 '11 at 19:55
  • 7
    @R..: Well, at least for me it's not. Reading from right to left, I get "pointer to const char". For me, it just feels better that way. – Xeo Feb 09 '11 at 20:49
  • 6
    Well you're fooling yourself because C types are read from the inside out, not from the left to right. :-) – R.. GitHub STOP HELPING ICE Feb 09 '11 at 20:54
  • 13
    I'm a little embarrassed I'm apparently the only one who doesn't understand this... but what's the difference between "the char **to** which it points" and "the char **at** which it points"? – Lack Jul 22 '13 at 17:40
  • 3
    Actually it is pretty easy to read and understand. Char **to** which it points -> Make the pointer point to a different place altogether. char **at** which it points -> modify the char the pointing is pointer at. No reason to be embarrassed though. The only part that is missing is that making the pointer itself not const isn't a big deal and is pretty harmless because it's passed by value (A copy of the address is passed) so changing it may not be appropriate but does not affect the user. – AturSams Apr 13 '14 at 11:51
  • 2
    Reading right to left can derp you up if you're reading code such as: `char const * const` vs. `const char * const` (both compilable and equivalent). A simple rule of thumb you can go by: const modifies (constifies?) the thing to its left. If nothing is there, then it's the thing to its right. – derpface Jul 29 '14 at 14:17
  • 8
    -1 for the unclarity; the answer is really unhelpful as written (although it doesn't deserve to be called idiotic). suggestion: change "const char* the_string : I can change the char to which the_string points, but I cannot modify the char at which it points." to "const char* the_string: I can change the pointer, but not the char at which it points." etc. – Don Hatch Aug 08 '14 at 06:01
  • For the function user, it doesn't matter if you use the verbose version or not because the real parameter is not going to change. So I prefer the "const char *" version. – Will Oct 23 '14 at 21:53
  • 5
    I found it difficult to understand the difference between "to which" and "at which" when I read the first time. – UXkQEZ7 Dec 31 '14 at 11:38
  • 2
    I have to say I prefer the answer by James Michael Hare to this one. The wording in this answer confuses me. – Armen Michaeli Jul 22 '16 at 17:16
  • First read Garrett's right-left rule answer then come back to read this will make more sense. – luochenhuan Aug 11 '17 at 16:03
  • Rather than "the char *to* vs *at* which it points", it would be clearer to say "the char to which it points" vs "to which char it points". – Raimund Krämer Nov 28 '18 at 11:25
  • This answer is well meaning but truly unintelligible.What were all the voters thinking? – Paul Childs Apr 28 '20 at 03:07
  • I find the current wording quite clear. This thread makes me think I should change how I declare constancy. eg instead of `const int foo` I should switch to `int const foo`, since that seems to be required to get a pointer that is const. in fact it seems like we should be doing `char const * const p` for consistency. – orion elenzil Nov 30 '21 at 21:29
164
  1. Mutable pointer to a mutable character

    char *p;
    
  2. Mutable pointer to a constant character

    const char *p;
    
  3. Constant pointer to a mutable character

    char * const p; 
    
  4. Constant pointer to a constant character

    const char * const p;
    
Community
  • 1
  • 1
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • Shouldn't this be: `const char* p; --> constant pointer to mutable character` and `char *const p; --> mutable pointer to constant character` – JamesWebbTelescopeAlien Dec 07 '15 at 18:33
  • 9
    @NulledPointer No. C++ declarations are formed right to left. You can thus read `const char * p` as: "p is a pointer to a character constant" or a mutable pointer to a constant character as James correctly states. same with the second :). – Samidamaru Dec 09 '15 at 09:34
33

const char * const means pointer as well as the data the pointer pointed to, are both const!

const char * means only the data the pointer pointed to, is const. pointer itself however is not const.

Example.

const char *p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //okay, changing the non-const pointer. 

const char * const p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //error, changing the const pointer. 
Nawaz
  • 353,942
  • 115
  • 666
  • 851
28

(I know this is old but I wanted to share anyway.)

Just wanted to elaborate on Thomas Matthews' answer. The Right-Left Rule of C type declarations pretty much says: when reading a C type declaration start at the identifier and go right when you can and left when you can't.

This is best explained with a couple examples:

Example 1

  • Start at the identifier, we can't go right so we go left

    const char* const foo
                ^^^^^
    

    foo is a constant...

  • Continue left

    const char* const foo
              ^
    

    foo is a constant pointer to...

  • Continue left

    const char* const foo
          ^^^^
    

    foo is a constant pointer to char...

  • Continue left

    const char* const foo
    ^^^^^
    

    foo is a constant pointer to char constant (Complete!)

Example 2

  • Start at the identifier, we can't go right so we go left

    char* const foo
          ^^^^^
    

    foo is a constant...

  • Continue left

    char* const foo
        ^
    

    foo is a constant pointer to...

  • Continue left

    char* const foo
    ^^^^
    

    foo is a constant pointer to char (Complete!)

Example 1337

  • Start at the identifier, but now we can go right!

    const char* const* (*foo[8])()
                            ^^^
    

    foo is an array of 8...

  • Hit parenthesis so can't go right anymore, go left

    const char* const* (*foo[8])()
                        ^
    

    foo is an array of 8 pointer to...

  • Finished inside parenthesis, can now go right

    const char* const* (*foo[8])()
                                ^^
    

    foo is an array of 8 pointer to function that returns...

  • Nothing more to the right, go left

    const char* const* (*foo[8])()
                     ^
    

    foo is an array of 8 pointer to function that returns a pointer to a...

  • Continue left

    const char* const* (*foo[8])()
                ^^^^^
    

    foo is an array of 8 pointer to functions that returns a pointer to a constant...

  • Continue left

    const char* const* (*foo[8])()
              ^
    

    foo is an array of 8 pointer to functions that returns a pointer to a constant pointer to a...

  • Continue left

    const char* const* (*foo[8])()
          ^^^^
    

    foo is an array of 8 pointer to functions that returns a pointer to a constant pointer to a char...

  • Continue left

    const char* const* (*foo[8])()
    ^^^^^
    

    foo is an array of 8 pointer to functions that returns a pointer to a constant pointer to a char constant (Complete!)

Further explanation: http://www.unixwiz.net/techtips/reading-cdecl.html

Garrett
  • 391
  • 4
  • 6
16

Many people suggest reading the type specifier from right to left.

const char * // Pointer to a `char` that is constant, it can't be changed.
const char * const // A const pointer to const data.

In both forms, the pointer is pointing to constant or read-only data.

In the second form, the pointer cannot be changed; the pointer will always point to the same place.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
3

const char * means that you can't use the pointer to change what is pointed to. You can change the pointer to point to something else, though.

Consider:

const char * promptTextWithDefault(const char * text)
{
    if ((text == NULL) || (*text == '\0'))
        text = "C>";
    return text;
}

The parameter is a non-const pointer to const char, so it can be change to another const char * value (like a constant string). If, however, we mistakenly wrote *text = '\0' then we'd get a compilation error.

Arguably, if you don't intend to change what the parameter is pointing to, you could make the parameter const char * const text, but it's not common to do so. We usually allow functions to change the values passed to parameters (because we pass parameters by value, any change does not affect the caller).

BTW: it is good practice to avoid char const * because it's often misread - it means the same as const char *, but too many people read it as meaning char * const.

TonyR
  • 31
  • 1
  • Wow! I was trying to figure out difference between my `const char *` and the signature `char const *` - the way you worded your BTW really helped! – sage Mar 23 '15 at 02:32
3

Nearly all of the other answers are correct, but they miss one aspect of this: When you use the extra const on a parameter in a function declaration, the compiler will essentially ignore it. For a moment, let's ignore the complexity of your example being a pointer and just use an int.

void foo(const int x);

declares the same function as

void foo(int x);

Only in the definition of the function is the extra const meaningful:

void foo(const int x) {
    // do something with x here, but you cannot change it
}

This definition is compatible with either of the declarations above. The caller doesn't care that x is const--that's an implementation detail that's not relevant at the call site.

If you have a const pointer to const data, the same rules apply:

// these declarations are equivalent
void print_string(const char * const the_string);
void print_string(const char * the_string);

// In this definition, you cannot change the value of the pointer within the
// body of the function.  It's essentially a const local variable.
void print_string(const char * const the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // COMPILER ERROR HERE
}

// In this definition, you can change the value of the pointer (but you 
// still can't change the data it's pointed to).  And even if you change
// the_string, that has no effect outside this function.
void print_string(const char * the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // OK, but not observable outside this func
}

Few C++ programmers bother to make parameters const, even when they could be, regardless of whether those parameters are pointers.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • "the compiler will essentially ignore it" not always true, Visual C++ 2015 would produce warning if you add the extra `const` to your function parameter in definition but not declaration. – raymai97 Sep 01 '18 at 20:26
  • 1
    @raymai97: I think that's a bug in the 2015 compiler, but I don't have 2015 handy to test. I works as I described in 2017, which, according to my conversations with some standards experts, is the expected behavior. – Adrian McCarthy Nov 19 '18 at 22:07
  • `const int t` would be an odd function declaration, which is why we have `readability-avoid-const-params-in-decls` in `clang-tidy`... I suspect that's what Visual C++ 2015 tried to implement (wild guess). – malat Mar 07 '22 at 08:40
2

There's no reason why either one wouldn't work. All print_string() does is print the value. It doesn't try to modify it.

It's a good idea to make function that don't modify mark arguments as const. The advantage is that variables that can't change (or you don't want to change) can be passed to these functions without error.

As far as the exact syntax, you want to indicate which type of arguments are "safe" to be passed to the function.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
2

The difference is that without the extra const the programmer could change, inside the method, where the pointer points to; for example:

 void print_string(const char * the_string)
 {
    cout << the_string << endl;
    //....
    the_string = another_string();
    //....

 }

That would be instead illegal if the signature were void print_string(const char * const the_string)

Many programmers feel too verbose (in most scenarios) the extra const keyword and omit it, even though it would be semantically correct.

leonbloy
  • 73,180
  • 20
  • 142
  • 190
1

I think it's vary rarely relevant, because your function isn't getting called with arguments like &*the_string or **the_string. The pointer itself is a value-type argument, so even if you modify it you're not going to change the copy that was used to call your function. The version you're showing ensures that the string will not change, and I think that's sufficient in this case.

Eglin
  • 591
  • 5
  • 10
1

In the latter, you are guaranteeing not to modify neither the pointer nor the characters. In the former, you only guarantee that the contents will not change, but you may move the pointer around.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
  • 1
    Ahh so without the final const, I could actually set the pointer to point to an entirely different string? – pict Feb 09 '11 at 19:07
  • Yes, without that final const you can use the parameter pointer to do some iteration by pointer arithmetic, where if there was that const you had to create your own pointer which is a copy of that parameter. – Jesus Ramos Feb 09 '11 at 19:12
-2

The difference between the two is that char* can point to any arbitrary pointer. Const char* by contrast, points to constants defined in the DATA section of the executable. And, as such, you cannot modify the character values of a const char* string.

Maz
  • 3,375
  • 1
  • 22
  • 27
  • 1
    I'm not asking the difference between char* and const char*. I'm asking between const char* and const char* const – pict Feb 09 '11 at 19:05
  • 2
    This answer is incorrect. A `const char*` can point anywhere it pleases. – Cubic Jun 15 '17 at 10:16