const char * constと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でした。これにはどちらが正しいでしょうか?

違いは、1つは定数文字へのポインターであり、もう1つは定数文字への定数ポインターであることです。しかし、なぜこれらの両方が機能するのでしょうか。それはいつ関連しますか?

回答:


244

後者を使用すると、the_string内部を変更できなくなりますprint_string。これは実際にはここで適切ですが、多分、開発者は冗長性を先延ばしにします。

char* the_string:私は変更することができますcharへのthe_stringポイントを、と私は変更することができchar、それが指し示すれます。

const char* the_string:私は変更することができますcharthe_stringのポイントを、私は変更することはできませんchar、それが指し示すれます。

char* const the_string:私は変更することはできませんcharthe_stringのポイントを、私は変更することができchar、それが指し示すれます。

const char* const the_string:私はcharどのthe_stringポイントを変更することはできません。また、どのポイントを変更することもできませんchar


11
最後の文の+1。const-correctnessは冗長ですが、それだけの価値があります。
mskfisher

6
@Xeo:フォームの意味が完全に変更されるのとは異なり、フォームはさらに混乱します。const char *const完全に反対側にあるため、はるかに優れています。
R .. GitHub ICE HELPING 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 characterおよび char *const p; --> mutable pointer to constant character
PnotNP

2
@NulledPointerいいえ。C++宣言は右から左に形成されます。したがってconst char * p、「pは文字定数へのポインタ」またはJamesが正しく述べているように定数文字への可変ポインタです。2番目と同じ:)。
Samidamaru

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ポインタの配列で ...

  • 左に進む

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

    fooは、charへの定数ポインターへのポインターを返す関数への8ポインターの配列です...

  • 左に進む

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

    fooは、char 定数への定数ポインターへのポインターを返す関数への8ポインターの配列です(完全!)

詳細説明:http : //www.unixwiz.net/techtips/reading-cdecl.html


CMIIW、const char * const fooはchar const * const foo?
luochenhuan 2017

@luochenhuanはい、そうです。
Garrett

12

タイプ指定子を右から左に読むことをお勧めします。

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

どちらの形式でも、ポインターは定数または読み取り専用データを指しています。

2番目の形式では、ポインターを変更できません。ポインターは常に同じ場所を指します。


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

他のほとんどすべての答えは正しいですが、これらの1つの側面が欠け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
}

constそれらがパラメーターであるかどうかに関係なく、たとえ可能であったとしても、パラメーターを作成することに煩わしいC ++プログラマーはほとんどいません。


「コンパイラーは本質的に無視する」とは限らないため、Visual C ++ 2015では、const宣言ではなく関数パラメーターにエクストラを追加すると警告が生成されます。
raymai97 2018

@ raymai97:2015コンパイラのバグだと思いますが、2015をテストするのに便利ではありません。私は2017年に説明したように動作しますが、標準の専門家との会話によると、これは予想される動作です。
エイドリアン・マッカーシー

-1

2つの違いは、char *が任意のポインターを指すことができることです。対照的に、const char *は、実行可能ファイルのDATAセクションで定義された定数を指します。そのため、const char *文字列の文字値を変更することはできません。


char *とconst char *の違いについては質問していません。私は*のconstのchar *とのconst char型の間のconst求めている
ピクト

この答えは間違っています。Aは、const char*それが喜ばどこでも指すことができます。
キュービック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.