const char * const vs const char *?


110

我正在运行一些示例程序来重新熟悉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。哪个更正确?

我知道区别在于一个是指向常量字符的指针,而另一个是指向常量字符的指针。但是为什么这两个都起作用?什么时候有意义?

Answers:


244

后者阻止您修改the_string内部print_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:我无法更改charthe_string指向的点,也无法修改char其指向的点。


11
+1为最后一句话。常量正确性是冗长的,但值得这样做。
mskfisher 2011年

6
@Xeo:您​​的表单更加令人困惑,因为它是与完全改变其含义相距的一种转换。const char *更好,因为const完全相反。
R .. GitHub停止帮助ICE,

7
@R ..:嗯,至少对我来说不是。从右到左阅读,我得到“指向const char的指针”。对我来说,那样感觉会更好。
Xeo

6
好吧,您在自欺欺人,因为C类型是从内而外而不是从左到右读取的。:-)
R .. GitHub停止帮助ICE,

11
我有点不好意思,我显然是唯一一个谁不明白这一点......但什么是“炭之间的区别,以它所指向”和“字符,在它指向的”?
缺少

138
  1. 指向可变字符的可变指针

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

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

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

    const char * const p;

这不应该是: const char* p; --> constant pointer to mutable characterchar *const p; --> mutable pointer to constant character
PnotNP

2
@NulledPointer否。C++声明从右到左形成。因此,您可以将其理解const char * p为:“ p是指向字符常量的指针”,或者是James正确指出的指向常量字符的可变指针。与第二个相同:)。
Samidamaru 2015年

28

const char * const表示指针以及指针指向的数据都是 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. 

20

(我知道这已经很老了,但我还是想分享。)

只是想详细说明托马斯·马修斯的答案。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 常量的常量指针(完成!)

例子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个指向函数的指针的数组,该函数返回一个指向 ... 的指针

  • 继续向左

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

    foo是一个由8个指向函数的指针组成的数组,这些函数返回一个指向常量的指针。

  • 继续向左

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

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

  • 继续向左

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

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

  • 继续向左

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

    foo是一个由8个指向函数的指针组成的数组,该函数返回一个指向常量指针的指针,该指针指向char 常量(Complete!)。

进一步说明:http : //www.unixwiz.net/techtips/reading-cdecl.html


CMIIW,const char * const foo应该等效于char const * const foo吗?
luochenhuan

@luochenhuan是的,的确如此。
加勒特

12

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

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

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

在第二种形式中,指针不能更改;指针将始终指向同一位置。


3

不同之处在于,无需额外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尽管在语义上是正确的,但许多程序员在大多数情况下都觉得多余的关键字过于冗长,因此忽略了它。


2

在后一种情况下,您保证不会在第一种情况下同时修改指针和字符,仅保证内容不会更改,但可以将指针移动


啊,如果没有最终的const,我实际上可以将指针设置为指向完全不同的字符串吗?
pict

是的,如果没有最终的const,则可以使用参数指针通过指针算术进行一些迭代,如果存在该const,则必须创建自己的指针,该指针是该参数的副本。
耶稣拉莫斯

2

没有理由为什么任何一个都不起作用。所有print_string()做的就是打印值。它不会尝试对其进行修改。

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

至于确切的语法,您想指出哪种类型的参数是“安全”的,可以传递给函数。


2

我认为这种变化很少相关,因为您的函数不会被&* the_string或** the_string这样的参数调用。指针本身是一个值类型的参数,因此即使您对其进行修改,也不会更改用于调用函数的副本。您显示的版本可确保字符串不会更改,在这种情况下,我认为足够了。


2

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的方式确实有帮助!
2015年

1

几乎所有其他答案都是正确的,但是它们却忽略了这一方面:当const在函数声明中的参数上使用extra 时,编译器实际上将忽略它。暂时,让我们忽略示例作为指针的复杂性,仅使用即可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'9

@ raymai97:我认为这是2015编译器中的错误,但我没有2015的方便测试工具。我按照2017年的描述工作,根据我与一些标准专家的对话,这是预期的行为。
Adrian McCarthy

-1

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


我不是在问char *和const char *之间的区别。我问的是const char *和const char * const之间
pict

这个答案是不正确的。一个const char*可以指向任何地方的地方。
立方
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.