web-dev-qa-db-ja.com

Const int *、const int * const、およびint const *の違いは何ですか?

私はいつもconst int*const int * const、そしてint const *の正しい使い方をめちゃくちゃにしています。できることとできないことを定義する一連の規則はありますか?

割り当て、関数への受け渡しなどの観点から、すべてのことやしないことを知りたいです。

1176
ultraman

逆方向に読んでください( 時計回り/スパイラルルール で動かされるように):

  • int* - intへのポインタ
  • int const * - const intへのポインタ
  • int * const - intへのconstポインタ
  • int const * const - const intへのconstポインタ

これで最初のconstは型のどちらの側にも置けるようになりました。

  • const int * == int const *
  • const int * const == int const * const

あなたが本当に夢中になりたいなら、あなたはこのようなことをすることができます:

  • int ** - intへのポインタへのポインタ
  • int ** const - intへのポインタへのconstポインタ
  • int * const * - intへのconstポインタへのポインタ
  • int const ** - const intへのポインタへのポインタ
  • int * const * const - intへのconstポインタへのconstポインタ
  • ...

そして、constの意味を明確にするために

const int* foo;
int *const bar; //note, you actually need to set the pointer 
                //here because you can't change it later ;)

fooは、定数整数への可変ポインタです。これにより、自分が指しているものを変更できますが、自分が指している値は変更できません。ほとんどの場合、これはconst charへのポインタがあるCスタイルの文字列で見られます。どの文字列を指すかは変更できますが、これらの文字列の内容を変更することはできません。これは、文字列自体がプログラムのデータセグメント内にあり、変更してはいけない場合に重要です。

barは、変更可能な値への定数ポインタまたは固定ポインタです。これは余分な構文糖がない参照のようなものです。このため、NULLポインタを許可する必要がない限り、通常はT* constポインタを使用する場所に参照を使用します。

1973
Matt Price

時計回り/螺旋の法則を知らない人のために:変数の名前から始めて、次の ポインタ または type に時計回りに(この場合は後ろに移動)移動してください。式が終了するまで繰り返します。

これがデモです。

pointer to int

const pointer to int const

pointer to int const

pointer to const int

const pointer to int

289
Shijing Lv

私はすべてここですでに答えられていると思います、しかし私はあなたがtypedefsに注意するべきであると付け加えたいです!それらは単なるテキストの置き換えではありません。

例えば:

typedef char *ASTRING;
const ASTRING astring;

astringの型はchar * constであり、const char *ではありません。これが私がいつもconstを型の右側に置く傾向がある理由の一つであり、決して始めにはありません。

134
Kaz Dragon

ほとんどの人が指摘したように:

const X* pX* const pconst X* const pの違いは何ですか?

ポインタ宣言は右から左に読む必要があります。

  • const X* pは、「pがconstであるXを指す」ことを意味します。Xオブジェクトは、pを介して変更することはできません。

  • X* const pは「pはconstではないXへのconstポインタ」を意味します。ポインタp自体は変更できませんが、pを介してXオブジェクトを変更することはできます。

  • const X* const pは、「pはconstであるXへのconstポインタ」を意味します。ポインタp自体を変更することも、pを介してXオブジェクトを変更することもできません。

48
luke
  1. 定数の参照:

    定数(ここではint)への参照。定数です。参照は実際の値よりサイズが小さいため、主に変数を参照として渡しますが、副作用があります。これは、実際の変数の別名のようなものだからです。エイリアスへのフルアクセスを通じて誤って主変数を変更する可能性があるため、この副作用を防ぐために、主変数を一定にします。

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    
  2. 定数ポインタ

    定数ポインタが変数を指すと、他の変数を指すことはできません。

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    
  3. 定数へのポインタ

    それが指す変数の値を変更することができないポインタは、定数へのポインタとして知られています。

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  4. 定数への定数ポインタ

    定数への定数ポインタは、それが指すアドレスを変更することも、そのアドレスに保持されている値を変更することもできないポインタです。

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    
42
Behrooz Tabesh

一般的な規則として、constキーワードはその直前のキーワードに適用されます。例外として、先頭のconstは以下のものに適用されます。

  • const int*int const*と同じで、 "定数intへのポインタ" を意味します。
  • const int* constint const* constと同じで、 "定数intへの定数ポインタ" を意味します。

編集: DosとDon'tsの場合、 この答え で十分ではない、あなたが望むものについてより正確になることができますか?

17
AProgrammer

この質問は 正確に を示していますなぜ私が私の質問で述べたように物事をやりたいのですか? type idの後のconstは許容できますか

手短に言えば、この規則を覚える最も簡単な方法は、 "const"がが適用されるものの後)になることです。したがって、 "int const *"はintが定数であるのに対して "int"は定数です。 * const "はポインタが定数であることを意味します。

誰かがそれを最前面に置くことを決定した場合(例: "const int *")、その場合の特別な例外として、それはそれ以降のものに適用されます。

彼らはそれがよりきれいに見えると思うので、多くの人がその特別な例外を使うのを好みます。私はそれが嫌いだ、なぜならそれは例外であり、そしてそれ故に物事を混乱させる。

16
T.E.D.

「const」の簡単な使い方

最も簡単な使い方は、名前付き定数を宣言することです。これを行うには、定数を変数のように宣言しますが、その前にconstを追加します。もちろん、後で値を変更することになるため、後で値を設定することはできないため、コンストラクタですぐに初期化する必要があります。例えば、

const int Constant1=96; 

想像もつかないほど 'Constant1'と呼ばれる値96の整数定数を作成します。

このような定数は、プログラムで使用されているがプログラムのコンパイル後に変更する必要がないパラメータには便利です。 Cプリプロセッサの '#define'コマンドよりもプログラマにとって利点があります。メインコンパイラに到達する前にプリプロセッサによってプログラムテキストに置き換えられるだけでなく、コンパイラ自体によって理解され使用されるため、エラーメッセージははるかに有用です。 。

これはポインタでも動作しますが、ポインタが指すのか、それが指すものが定数であるのか、あるいはその両方なのかを判断するには、constの場所に注意する必要があります。例えば、

const int * Constant2 

constant2が定数整数への可変ポインタであることを宣言し、

int const * Constant2

は同じことをする代替構文ですが、

int * const Constant3

constant3が可変整数への定数ポインタであり、かつ

int const * const Constant4

constant4が定数整数への定数ポインタであることを宣言します。基本的に「const」はそのすぐ左にあるものすべてに適用されます(その場合、そのすぐ右にあるものすべてに適用されるものがない場合を除く)。

ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

14
ufukgun

C++ Guru Scott Meyersによるこの book に出会うまで、私はあなたと同じ疑問を抱いていました。この本の3番目の項目を参照してください。彼はconstの使い方について詳しく述べています。

このアドバイスに従ってください

  1. 単語constがアスタリスクの左側に表示されている場合、指しているのは定数です。
  2. 単語constがアスタリスクの右側に表示される場合、ポインタ自体は定数です。
  3. constが両側にある場合、両方とも定数です
7
rgk

CとC++の宣言構文は、元の設計者によって、失敗した実験として繰り返し説明されてきました。

代わりに、 name 型「Typeへのポインタ」としましょう。これをPtr_と呼びます。

template< class Type >
using Ptr_ = Type*;

Ptr_<char>charへのポインタです。

Ptr_<const char>const charへのポインタです。

そしてconst Ptr_<const char>const charへのconstポインタです。

そこ。

enter image description here

それは簡単ですがトリッキーです。 const修飾子は任意のデータ型(intcharfloatなど)と交換できることに注意してください。

以下の例を見てみましょう。


const int *p ==> *pは読み取り専用です[pは定数整数へのポインタです]

int const *p ==> *pは読み取り専用です[pは定数整数へのポインタです]


int *p const ==> 間違った ステートメント。コンパイラは構文エラーをスローします。

int *const p ==> pは読み取り専用です[pは整数への定数ポインタ]。ここでのポインタpは読み取り専用なので、宣言と定義は同じ場所になければなりません。


const int *p const ==> 間違った ステートメント。コンパイラは構文エラーをスローします。

const int const *p ==> *pは読み取り専用です

const int *const p1 ==> *ppは読み取り専用です[pは定数整数への定数ポインタ]。ここでのポインタpは読み取り専用なので、宣言と定義は同じ場所になければなりません。


int const *p const ==> 間違った ステートメント。コンパイラは構文エラーをスローします。

int const int *p ==> 間違った ステートメント。コンパイラは構文エラーをスローします。

int const const *p ==> *pは読み取り専用で、int const *pと同じです。

int const *const p ==> *ppは読み取り専用です[pは定数整数への定数ポインタ]。ここでのポインタpは読み取り専用なので、宣言と定義は同じ場所になければなりません。

5
Abhijit Sahu

C++におけるconstの正しさを取り巻く微妙な点が他にもたくさんあります。ここでの質問は単にCに関するものであると思いますが、タグはC++なので、いくつかの関連例を挙げます。

  • 文字列のような大きな引数をTYPE const &として渡すと、オブジェクトの変更やコピーができなくなります。例:

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    しかし、TYPE & constは無意味です。なぜなら参照は常にconstだからです。

  • クラスを変更しないクラスメソッドは常にconstとしてラベル付けする必要があります。そうしないと、TYPE const &参照からメソッドを呼び出すことができません。例:

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • 戻り値とメソッドの両方がconstであるべき一般的な状況があります。例:

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    実際、constメソッドは、内部クラスデータを非constへの参照として返してはいけません。

  • その結果、多くの場合、constオーバーロードを使用して、constメソッドと非constメソッドの両方を作成する必要があります。例えば、もしあなたがT const& operator[] (unsigned i) const;を定義するならば、あなたはおそらくまた以下によって与えられる非constバージョンが欲しいでしょう:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

残念ながら、Cにはconst関数はありません、C++では非メンバ関数自体をconstにすることはできません。constメソッドには副作用があり、コンパイラはconst関数を使用して関数呼び出しの重複を避けることはできません。実際、単純なint const &参照でさえ、それが参照する値が他の場所で変更されているのを目にするかもしれません。

5
Jeff Burdges

私にとっては、constの位置、つまり*に対して左右どちらに表示されるか、左右両方に表示されるかによって、実際の意味を理解するのに役立ちます。

  1. *のLEFTへのconstは、ポインターが指すオブジェクトがconstオブジェクトであることを示します。

  2. *の右へのconstは、ポインターがconstポインターであることを示します。

次の表はStanford CS106L Standard C++プログラミングラボコースリーダーの抜粋です。

enter image description here

3
srivatsahc

両側にintがあるconstは ポインタを定数int にします。

const int *ptr=&i;

または

int const *ptr=&i;

'*'の後のconstは 定数ポインタをint にします。

int *const ptr=&i;

この場合、これらすべては 定数整数へのポインタ ですが、これらはどれも定数ポインタではありません。

 const int *ptr1=&i, *ptr2=&j;

この場合、すべてが 定数整数 へのポインタで、ptr2が 定数整数への定数ポインタ です。しかしptr1は定数ポインタではありません。

int const *ptr1=&i, *const ptr2=&j;
2
Hunter

これは、主に2番目の行(ベストプラクティス、割り当て、関数パラメーターなど)に対応しています。

一般的な練習。 constできるすべてのものを作成してください。または、別の言い方をすれば、すべてをconstにして、プログラムを機能させるために必要なconstsの最小セットを完全に削除します。これは、const-correctnessを達成する上で大きな助けとなり、人々が修正するべきではないものに割り当てようとするときに微妙なバグが導入されないようにするのに役立ちます。

ペストのようなconst_cast <>は避けてください。それには1つまたは2つの正当な使用例がありますが、それらはごくわずかであり、その間でもありません。 constオブジェクトを変更しようとしている場合、最初のペースでconstを宣言した人を見つけて、何についてのコンセンサスを得るために彼らと話し合うかにより良い結果が得られます。起こるはずです。

これは非常にきちんと割り当てにつながります。非constである場合にのみ、何かに割り当てることができます。 constに割り当てる場合は、上記を参照してください。宣言ではint const *foo;int * const bar;異なるものがconstであることを覚えておいてください-ここでの他の答えは見事にその問題をカバーしているので、私はそれに入りません。

関数パラメーター:

値渡し:例void func(int param)呼び出し側でどちらの方法でもかまいません。関数をvoid func(int const param)として宣言するユースケースがあることを引数にできますが、呼び出し中は関数自体にのみ影響し、呼び出し中に関数によって渡される値は変更できません。 。

参照渡し:例void func(int &param)これで違いが生じます。宣言されたようにfuncparamの変更を許可されており、呼び出し元のサイトは結果に対処する準備ができているはずです。宣言をvoid func(int const &param)に変更すると、契約が変更され、funcparamを変更できなくなることが保証されます。つまり、渡されるものが戻ってくることを意味します。他の人が指摘したように、これは変更したくない大きなオブジェクトを安く渡すために非常に便利です。参照を渡すことは、大きなオブジェクトを値で渡すよりもはるかに安価です。

ポインターで渡す:例void func(int *param)およびvoid func(int const *param)これら2つは、参照の対応物とほぼ同義です。ただし、他の契約上の保証によりnullptrが保証されない限り、呼び出された関数はfuncをチェックする必要がありますnullptrparamを受け取ることはありません。

そのトピックに関する意見書。このようなケースで正しさを証明することは地獄のように困難であり、間違いを犯すのは簡単すぎます。したがって、チャンスをとらないで、nullptrのポインターパラメーターを常に確認してください。あなたは自分自身の痛みと苦しみを救い、長期的にはバグを見つけるのは難しいでしょう。また、チェックのコストに関して言えば、コストは安く、コンパイラーに組み込まれた静的分析で管理できる場合は、オプティマイザーがそれを排除します。 MSVCのLink Time Code Generation、またはGCCのWOPR(私は思う)をオンにすると、プログラム全体で、つまりソースコードモジュールの境界を越える関数呼び出しでも使用できるようになります。

結局のところ、上記のすべてが常にポインターへの参照を好むという非常に堅実なケースを作ります。ずっと安全です。

1
dgnuff

他の説明に従ってCを完全にするためだけにあり、C++の場合は不明です。

  • pp - ポインタへのポインタ
  • p - ポインタ
  • data - 例で指摘されていることx
  • 太字 - 読み取り専用変数

ポインタ

  • pデータ - int *p;
  • p データ - int const *p;
  • p data - int * const p;
  • p data - int const * const p;

ポインタへのポインタ

  1. pp p data - int **pp;
  2. pp pデータ - int ** const pp;
  3. pp p data - int * const *pp;
  4. pp p data - int const **pp;
  5. pp p data - int * const * const pp;
  6. pp p data - int const ** const pp;
  7. pp p data - int const * const *pp;
  8. pp p データ - int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

間接参照のNレベル

ただ進み続けなさい、しかし人類があなたを非難するかもしれない.

 int x = 10;
 int *p = &x;
 int **pp = &p;
 int ***ppp = &pp;
 int ****pppp = &ppp;

 printf("%d \n", ****pppp);
1
  • const左へ of *の場合、値を参照します(const intであるかint constであるかは関係ありません)
  • const右へ of *の場合、ポインター自体を参照します
  • 同時に両方にすることができます

重要なポイント:const int *p参照している値が一定であることを意味しない!!。これは変更できないことを意味しますそのポインターを使用。値自体は変更される場合があります。例えば

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

これは、関数が渡される引数を変更できないことを保証するために、主に関数シグネチャで使用されることを意図しています。

0
blue_note