开发者

`const char * const` versus `const char *`?

开发者 https://www.devze.com 2023-02-09 19:23 出处:网络
I\'m running through some examp开发者_JAVA百科le programs to refamiliarize myself with C++ and I have run into the following question.First, here is the example code:

I'm running through some examp开发者_JAVA百科le 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?


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.


  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;
    


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. 


(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


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.


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.


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.


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.


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.


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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号