const修飾子についてまとめてみます。
const修飾子とは
const修飾子とは、その変数の値を変更してはいけない(つまりは定数である)、ということを示す修飾子です。宣言の型名の部分の前か後につけて使います。例えば、次のような感じになります。
int test()
{
const int x=3;
printf("%d\n",x); //定数を書き換えるわけではないので問題なし
x=4; //定数を書き換えようとしているのでコンパイルエラー(error: assignment of read-only variable ‘x’)
return x;
}
なお、個人的な感覚としては、const修飾子は前につけるのが一般的なのではないかと思います。
ポインタ変数におけるconst修飾子とは
ポインタ変数におけるconst修飾子は付ける位置によって、次の3つに分かれます。
ポインタ変数からの変数の書き換えをできなくする
const修飾子を前の章と同じように使うと、ポインタ変数からの変数の書き換えができなくなります。例えば、次のようになります。
int *test()
{
int i=2,j=3;
const int *p=&i;
int *t=p; //tがpの指す変数を指してしまうと書き換え不可という規則が崩れるので、コンパイル時に警告が出る(warning: initialization discards ‘const’ qualifier from pointer target type)
int *s=&i; //sがpの指す変数を指してしまうと書き換え不可という規則が崩れるが、pがiを指しているかどうかをコンパイラは把握していないのでコンパイルが通る
const int *q=p; //qがpの指す変数を指しても書き換え不可という規則は崩れないので問題なし
p=&j; //ポインタ変数自体にconst修飾子がかかっているわけではないので問題なし
*p=0; //const修飾子がついているポインタ変数から変数を書き換えようとしているのでコンパイルエラー(error: assignment of read-only location ‘*p’)
*s=0; //const修飾子がついていないポインタ変数からpが指している変数を書き換えているので問題なし
printf("%d\n",*p); //書き換え不可という規則は崩れないので問題なし
return p; //pの指す変数を返してしまうと書き換え不可という原則が崩れてしまうので、コンパイル時に警告が出る(warning: return discards ‘const’ qualifier from pointer target type)
}
この使い方のポイントは以下のようになります。
- ポインタ変数が指している変数の書き換えができないという原則が崩れているかどうか
- ポインタ変数が指している変数の書き換えができないという原則が崩れている場合、それをコンパイラが把握しているかどうか
ポインタ変数の書き換えをできなくする
const修飾子を宣言のアスタリスクと変数名の間に入れると、ポインタ変数自体の書き換えができなくなります。例えば、次のようになります。
int *test()
{
int i=2,j=3;
int * const p=&i;
int *t=p; //pを書き換えるわけではないので問題なし
int *s=&i; //pを書き換えるわけではないので問題なし
const int *q=p; //pを書き換えるわけではないので問題なし
p=&j; //ポインタ変数自体が書き換え不可なのでコンパイルエラー(error: assignment of read-only variable ‘p’)
*p=0; //pを書き換えるわけではないので問題なし
*s=0; //pを書き換えるわけではないので問題なし
printf("%d\n",*p); //pを書き換えるわけではないので問題なし
return p; //pを書き換えるわけではないので問題なし
}
この使い方のポイントは以下のようになります。
- ポインタ変数自体を書き換えるかどうか
ポインタ変数からの変数の書き換えとポインタ変数の書き換えをできなくする
前の2つの使い方を合わせた使い方です。例えば、次のようになります。
int *test()
{
int i=2,j=3;
const int * const p=&i;
int *t=p; //tがpの指す変数を指してしまうと書き換え不可という規則が崩れるので、コンパイル時に警告が出る(warning: initialization discards ‘const’ qualifier from pointer target type)
int *s=&i; //sがpの指す変数を指してしまうと書き換え不可という規則が崩れるが、pがiを指しているかどうかをコンパイラは把握していないのでコンパイルが通る
const int *q=p; //qがpの指す変数を指しても書き換え不可という規則は崩れないので問題なし
p=&j; //ポインタ変数自体が書き換え不可なのでコンパイルエラー(error: assignment of read-only variable ‘p’)
*p=0; //const修飾子がついているポインタ変数から変数を書き換えようとしているのでコンパイルエラー(error: assignment of read-only location ‘*p’)
*s=0; //const修飾子がついていないポインタ変数からpが指している変数を書き換えているので問題なし
printf("%d\n",*p); //書き換え不可という規則は崩れないので問題なし
return p; //pの指す変数を返してしまうと書き換え不可という原則が崩れてしまうので、コンパイル時に警告が出る(warning: return discards ‘const’ qualifier from pointer target type)
}
この使い方のポイントは以下のようになります。
- ポインタ変数が指している変数の書き換えができないという原則が崩れているかどうか
- ポインタ変数が指している変数の書き換えができないという原則が崩れている場合、それをコンパイラが把握しているかどうか
- ポインタ変数自体を書き換えるかどうか
結び
ここで挙げた例はgccでWall
オプションをつけてコンパイルすると、宣言した変数が使われてないと怒られてしまう例ばかりですが、そこをなんとかしようとすると、例示するプログラムとしては複雑になりすぎてしまうのでご容赦ください。
ポインタ変数のconst修飾子に関しては勉強しながらまとめたので間違っているところがあるかもしれません。もし、間違いに気づきましたらコメント欄でご指摘願います。