元々C言語にはconstがありませんでしたが、C++から逆輸入されて、多くのCコンパイラに実装されています。XC8にもあります。
constは普通の変数の宣言に使えます。
const int x = 10;
これはxを定数として宣言しています。
int main() {
const int x = 10;
x = 20; // エラー
getchar();
}
これは変更しようとしているのでエラーです。
初期化時には代入できます。逆に初期化しなければエラーデス!!。
int main() {
const int x; // エラー
x = 20; // エラー
getchar();
}
さて、引数に取ってみましょう。次のような使い方もできます。
void f(const int x) {
x = 10; // エラー
}
constが付いている変数xは変更できません。なお、この場合はxを変更したとしてもプログラムの内容に支障を来さないので付けないのが習慣です。次の例ではconstが多用されます。
void f(const int* x) {
int a = 10;
x = &a; // エラーではない!
}
これは、ポインタ変数xが定数ではなく、ポインタ変数xが指す中身が定数だと言っているのです。
つまり、次の例はエラーとなります。
void f(const int* x) {
*x = 10; // xの表す先を書き換えようとしているのでエラー
}
そのほか、前と同様にポインタ変数の変更を禁止することもできます。constの位置に注意。
最も変数に近いようにcosntを付けるとこのような扱いになります。
void f(int* const x) {
int a = 200;
*x = 10; // エラーではない!
x = &a; // エラー!
}
普通の変数の場合と同様、変更したことで生じるデメリットはないので普通はこのような書き方はしません。
とはいっても、ポインタを直接書き換えることはあまりないので予防線を張る意味でもこのような書き方をする人もいます。ぶっちゃけ常に自動的にint* const xとしてくれてもいいくらいです。
ポインタと絡めることで有用性が発揮されます。
Tips1に出てきた
void (*(*p)[3])(void);
に対してconstを適用してみましょう。
一体何に対してconstするのでしょうか?
1. ポインタ変数p自体
2. ポインタ変数pの指す配列
まず1.ですが、この場合は変数に最も近い位置に書くのでしたね。
void (* (* const p)[3])(void) = { &a };
これで、変数pの変更が禁止されました。
初期化しないといけないので実体であるaのポインタを入れて初期化しています。但し、要素1, 2については省略しました。
以下ハロワの例です。
void f(void) {
printf("hello, world!");
}
int main() {
void (* a[3])(void);
void (* (* const p)[3])(void) = { &a };
a[0] = f;
(*p)[0]();
getchar();
}
次に2.ですが、配列自体の変更を禁止したいと思います。
つまり、配列の要素の変更の禁止ですね。
void (* const (*p)[3])(void);
配列(*p)[3]に対してconstを直前に持ってきて変更を禁止しています。
int main() {
void (* a[3])(void);
void (* const (*p)[3])(void);
p = &a;
(*p)[0] = f; // エラー!!
(*p)[0]();
getchar();
}
なお、配列は元々変更禁止なので、
int (const a)[3];
int b[3];
a = b;
といったことはできません。
同様の理由で
void (*(const *p)[3])(void);
はエラーです。このような書き方はありません。
関数自体は書き換えられません。従って
void (const *p)(void);
のような書き方はありません。
同様の理由で
void (const *(*p)[3])(void);
はエラーとなります。
なお、
void const (*(*p)[3])(void);
はエラーではありませんが、意味はありません。
constは安全のためにもよく使うように心がけましょう。
0 件のコメント:
コメントを投稿