超絶初歩的だが、const
をつけた時のポインタの表す意味がたまにわからなくなるので自分用メモ。
int型へのポインタpは以下のように書ける
int * p;
この時*
が型もしくは変数名についていることがあるが、それについては特に区別しない。
つまり、
[型名] * [変数名];
と書けるわけだが、このポインタ変数にconst
をつけることを考えると、候補となる位置は
(1) [型名] (2) * (3) [変数名] (4);
が有り得そうだ。そして、実際に(1),(2),(3)の位置にはconst
を置くことが出来る。
そして、このconst
の位置によって、何にconst
を付与しているかの意味が変わってくる。
具体的には、
- ポインタ変数が保持している対象の変数のアドレス(=ポインタ変数の値そのもの)
- ポインタ変数によってアドレスが保持されているオブジェクト(=ポインタ変数が指す先)
に対するconst
の付与という2種類の付与の意味がある。ここでは前者を「指示変数」、後者を「被指示変数」と呼ぶことにする(一般的な呼び方ではなく、文章が長くなるのを避ける為の便宜的なもの)。
実際に、上記(1),(2),(3)の3箇所について、const
を置くことを考えると
//const1つ const int * p; int const * p; int * const p; //const2つ const int const * p; const int * const p; int const * const p; //const3つ const int const * const p;
の組み合わせが考えられるが、これらそれぞれについて表にすると、
宣言 | 指示変数の書き換え | 被指示変数の書き換え |
---|---|---|
//const1つ | ||
const int * p; | ○ | × |
int const * p; | ○ | × |
int * const p; | × | ○ |
//const2つ | ||
const int const * p; | - | - |
const int * const p; | × | × |
int const * const p; | × | × |
//const3つ | ||
const int const * const p; | - | - |
となる(-になっているところはそもそも定義不能)。
簡単な確認コードは以下。
int main(){ int hoge = 0; int * ip = &hoge; const int * cip = &hoge; int const * icp = &hoge; int * const ipc = &hoge; //const int const * cicp = &hoge;//error: duplicate 'const' const int * const cipc = &hoge; int const * const icpc = &hoge; //const int const * const cicpc = &hoge; //error: duplicate 'const' int fuga = 1; cip = &fuga; //*cip = 2; //error: assignment of read-only location '* cip' icp = &fuga; //*icp = 2; //error: assignment of read-only location '* icp' //ipc = &fuga; //error: assignment of read-only variable 'ipc' *ipc = 2; //cipc = &fuga; //error: assignment of read-only variable 'cipc' //*cipc = 2; //error: assignment of read-only location '*(const int*)cipc' //icpc = &fuga; //error: assignment of read-only variable 'icpc' //*icpc = 2; //error: assignment of read-only location '*(const int*)icpc' }
ここまで見るとややこしいように見えるかもしれないが、簡単な覚え方があって、*
より前のconst
は被指示変数の書き換えを禁止し、*
より後のconst
は指示変数の書き換えを禁止すると覚えるといい。
因みに上の確認コードのコメントにもあるように、同じ意味のconst
付与が重複するとエラーになるので注意。
*
より前のconst
って指示変数と被指示変数、どっちの書き換えを禁止するんだっけ…となったら参照を思い出すといいかも。const
付き参照を書く時は
const int ref& = hoge;
となるが、参照はどのオブジェクトを参照するかの書き換えはconst
がついていなくても出来ない(=ポインタと同じように考えるなら指示変数は書き換えできないということ)。つまり、わざわざconst
をつけるという事は、参照先の被指示変数の書き換えを禁止していることを意味している。
これをポインタの先程の覚え方と同じように「&
より前にconst
がある時は参照先の被指示変数を書き換えられなくする」と無理矢理に解釈すれば、「ポインタも*
より前にconst
がある時は被指示変数を書き換えられなくする」と思い出せる。
以上はオブジェクトへのポインタの意味の覚え方だが、もう少し汎用的にポインタを読めるようになりたいのであれば、ポインタ完全制覇に載っているので、こちらを読むことをおすすめする。