【问题标题】:`const char * const` versus `const char *`?`const char * const` 与 `const char *`?
【发布时间】:2011-02-09 19:02:20
【问题描述】:

我正在运行一些示例程序以重新熟悉 C++,我遇到了以下问题。首先,这里是示例代码:

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

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

在上面的代码中,print_string 的参数可能是const char * const the_string。哪个更正确?

我知道区别在于一个是pointer to a constant character,而另一个是constant pointer to a constant character。但为什么这两种方法都有效?什么时候相关?

【问题讨论】:

    标签: c++


    【解决方案1】:

    后者阻止您在print_string 中修改the_string。在这里实际上是合适的,但可能是冗长推迟了开发人员。

    char* the_string :我可以更改char the_string 指向的位置,也可以修改char 指向的位置。

    const char* the_string :我可以更改char the_string 指向的位置,但我无法修改它指向的char

    char* const the_string :我无法更改char the_string 指向的位置,但我可以修改它指向的char

    const char* const the_string :我无法更改char the_string 指向的位置,也无法修改它指向的char

    【讨论】:

    • +1 表示最后一句话。常量正确性很冗长,但非常值得。
    • @Xeo:您​​的表格更加令人困惑,因为它是一个完全改变其含义的换位。 const char * 更好,因为 const 完全相反。
    • @R..:嗯,至少对我来说不是。从右到左阅读,我得到“指向 const char 的指针”。对我来说,这样感觉更好。
    • 我有点尴尬,我显然是唯一一个不明白这一点的人......但是“它所指向的字符 to 之间有什么区别”和“它指向的字符 at”?
    • -1 表示不清晰;答案在书面上确实没有帮助(尽管它不应该被称为白痴)。建议:更改“const char* the_string:我可以更改 the_string 指向的字符,但我无法修改它指向的字符。”改为“const char* the_string:我可以更改指针,但不能更改它指向的字符。”等
    【解决方案2】:
    1. 指向可变字符的可变指针

      char *p;
      
    2. 指向常量字符的可变指针

      const char *p;
      
    3. 指向可变字符的常量指针

      char * const p; 
      
    4. 指向常量字符的常量指针

      const char * const p;
      

    【讨论】:

    • 不应该是:const char* p; --&gt; constant pointer to mutable characterchar *const p; --&gt; mutable pointer to constant character
    • @NulledPointer 否。C++ 声明是从右到左形成的。因此,您可以将const char * p 解读为:“p 是指向字符常量的指针”或 James 正确声明的指向常量字符的可变指针。与第二个相同:)。
    【解决方案3】:

    const char * const 表示指针以及指针指向的数据,both const!

    const char * 表示只有指针指向的数据是 const。然而,指针本身不是 const。

    示例。

    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. 
    

    【讨论】:

      【解决方案4】:

      (我知道这是旧的,但我还是想分享。)

      只是想详细说明 Thomas Matthews 的回答。 C 类型声明的左右规则几乎说:当阅读 C 类型声明时,从标识符开始,可以时向右,不能时左。

      这最好用几个例子来解释:

      示例 1

      • 从标识符开始,我们不能向右走,所以我们向左走

        const char* const foo
                    ^^^^^
        

        foo 是一个常量...

      • 继续向左

        const char* const foo
                  ^
        

        foo 是一个常量指针...

      • 继续向左

        const char* const foo
              ^^^^
        

        foo 是一个指向 char...

        的常量指针
      • 继续向左

        const char* const foo
        ^^^^^
        

        foo 是一个指向 char constant 的常量指针(完成!)

      示例 2

      • 从标识符开始,我们不能向右走,所以我们向左走

        char* const foo
              ^^^^^
        

        foo 是一个常量...

      • 继续向左

        char* const foo
            ^
        

        foo 是一个常量指针...

      • 继续向左

        char* const foo
        ^^^^
        

        foo 是一个指向 char 的常量指针(完成!)

      示例 1337

      • 从标识符开始,但现在我们可以继续前进了!

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

        foo 是一个 8 个数组...

      • 打括号,所以不能再向右了,向左走

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

        foo 是一个由 8 个指针组成的数组...

      • 括号内完成,现在可以向右了

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

        foo 是一个包含 8 个指针的数组,指向 返回的函数...

      • 右边没有了,往左走

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

        foo 是一个包含 8 个函数指针的数组,该函数返回 一个指向 a 的指针...

      • 继续向左

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

        foo 是一个包含 8 个函数指针的数组,它返回一个指向 constant...

        的指针
      • 继续向左

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

        foo 是一个由 8 个指向函数的指针组成的数组,该函数返回一个指向常量的指针指向 a 的指针...

      • 继续向左

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

        foo 是一个包含 8 个函数指针的数组,该函数返回一个指向 char...

        的常量指针的指针
      • 继续向左

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

        foo 是一个包含 8 个函数指针的数组,它返回一个指向 char 的常量指针的指针 constant(完成!)

      进一步解释:http://www.unixwiz.net/techtips/reading-cdecl.html

      【讨论】:

      • CMIIW, const char* const foo 应该等价于 char const * const foo?
      • @luochenhuan 是的,确实是。
      【解决方案5】:

      很多人建议从右到左阅读类型说明符。

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

      在这两种形式中,指针都指向常量或只读数据。

      第二种形式,指针不能改变;指针总是指向同一个地方。

      【讨论】:

        【解决方案6】:

        const char * 表示您不能使用指针来更改指向的内容。不过,您可以将指针更改为指向其他内容。

        考虑:

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

        该参数是一个指向 const char 的非 const 指针,因此可以将其更改为另一个 const char * 值(如常量字符串)。但是,如果我们错误地写了*text = '\0',那么我们会得到一个编译错误。

        可以说,如果您不打算更改参数指向的内容,则可以将参数设为const char * const text,但这样做并不常见。我们通常允许函数改变传递给参数的值(因为我们是按值传递参数,所以任何改变都不会影响调用者)。

        顺便说一句:避免char const * 是一种很好的做法,因为它经常被误读——它的含义与const char * 相同,但太多人将其解读为char * const

        【讨论】:

        • 哇!我试图找出我的 const char * 和签名 char const * 之间的区别 - 你用 BTW 措辞的方式真的很有帮助!
        【解决方案7】:

        几乎所有其他答案都是正确的,但他们忽略了这一点:当您在函数声明中的参数上使用额外的 const 时,编译器基本上会忽略它。暂时,让我们忽略作为指针的示例的复杂性,只使用int

        void foo(const int x);
        

        声明与

        相同的功能
        void foo(int x);
        

        只有在函数的定义中,多余的const才有意义:

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

        此定义与上述任一声明兼容。调用者并不关心xconst——这是一个与调用站点无关的实现细节。

        如果您有一个指向 const 数据的 const 指针,则适用相同的规则:

        // 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
        }
        

        很少有 C++ 程序员会费心制作参数 const,即使他们可以这样做,无论这些参数是否是指针。

        【讨论】:

        • “编译器将基本上忽略它”并不总是正确的,如果您在定义而不是声明中将额外的 const 添加到函数参数中,Visual C++ 2015 会产生警告。
        • @raymai97:我认为这是 2015 编译器中的一个错误,但我没有 2015 方便测试。我按照我在 2017 年的描述工作,根据我与一些标准专家的对话,这是预期的行为。
        • const int t 将是一个奇怪的函数声明,这就是为什么我们在 clang-tidy 中有 readability-avoid-const-params-in-decls... 我怀疑这就是 Visual C++ 2015 试图实现的(疯狂猜测)。跨度>
        【解决方案8】:

        没有任何理由不工作。 print_string() 所做的只是打印值。它不会尝试修改它。

        制作不将标记参数修改为 const 的函数是个好主意。优点是不能更改(或您不想更改)的变量可以毫无错误地传递给这些函数。

        就确切的语法而言,您希望指出哪些类型的参数是“安全的”可以传递给函数。

        【讨论】:

          【解决方案9】:

          不同之处在于,如果没有额外的const,程序员可以在方法内部更改指针指向的位置;例如:

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

          如果签名是void print_string(const char * const the_string),那将是非法的

          许多程序员觉得多余的 const 关键字过于冗长(在大多数情况下)并忽略它,即使它在语义上是正确的。

          【讨论】:

            【解决方案10】:

            在后者中,您保证不会在第一个中同时修改指针和字符,您只保证内容不会改变,但您可以移动指针

            【讨论】:

            • 啊,所以没有最终的 const,我实际上可以将指针设置为指向一个完全不同的字符串?
            • 是的,如果没有最终的 const,您可以使用参数指针通过指针算术进行一些迭代,如果有该 const,您必须创建自己的指针,该指针是该参数的副本。
            【解决方案11】:

            我认为它很少有相关性,因为您的函数没有使用 &*the_string 或 **the_string 之类的参数调用。指针本身是一个值类型参数,因此即使您修改它,也不会更改用于调用函数的副本。您显示的版本确保字符串不会更改,我认为在这种情况下就足够了。

            【讨论】:

              【解决方案12】:

              两者的区别在于 char* 可以指向任意指针。相反,Const char* 指向可执行文件的 DATA 部分中定义的常量。因此,您不能修改 const char* 字符串的字符值。

              【讨论】:

              • 我不是在问 char* 和 const char* 之间的区别。我在 const char* 和 const char* const 之间询问
              • 这个答案不正确。 const char* 可以指向任何它喜欢的地方。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-11-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多