C言語では型にconstをつけることで、誤って値を代入しようとした場合にコンパイルエラーを発生させることができるが、
ポインタの場合constを置く位置によって変数自体かポインタの指す先かを制御できる。
非ポインタ型の場合
const int i = 1; |
ポインタじゃない型の場合には変数の値を定数として扱い、再代入をコンパイラがエラーにして防いでくれる。
const intでもint constの順でも意味は同じ。
単独のポインタの場合
ポインタが絡むとconstを置く位置によって何の上書きが禁止になるかが異なる:
const char* s11 = "foo"; |
- 文字列だと
constを置く位置はアスタリスクの前後の2通りがある。 - 前:
s11はポインタが指す中身が書き換えられないことを保証する。 ポインタ変数の値自体は変更できる。s12はs11と同じ。
- 後:
s2は逆にポインタが指す中身を書き換えられるが、ポインタ変数自体は再代入不可。 (ただし上の例だとs2が指している文字列"foo"自体は上書き不可能なデータなので、コンパイルは通るが実行すると強制終了する。) - 前後:
s3が完全な定数となる。
二重ポインタの場合
例えばC言語では文字列が char へのポインタなので、文字列の配列は二重ポインタ char** になる。
それに対する const を置ける位置は3ヶ所なので、組み合わせは2の3乗で8通り:
void func1(const char** buf1) { |
func1のbuf1は文字列は上書き不可、配列とポインタは可constとcharを入れ替えた場合は同じなので省略
buf2は配列は上書き不可、文字列とポインタは可buf3は文字列と配列は上書き不可、ポインタは可buf4はポインタは上書き不可、文字列と配列は可- 以下[
buf4]×[buf1,buf2,buf3]で組み合わせ
考え方
プリミティブな型の前でも後でも意味は変わらないので後に置くことにして(ex. char const)、
constより左の内容が上書き不可になる:
| 文字 | 文字列 | 文字列配列 | |
|---|---|---|---|
| char const** buf1 | 不可 | 可 | 可 |
| char * const* buf2 | 可 | 不可 | 可 |
| char const* const* buf3 | 不可 | 不可 | 可 |
| char ** const buf4 | 可 | 可 | 不可 |
でもまあ実際は何個も書くのが面倒なので、「書き換え不可な文字列配列」を意図してても単にconst char**にしちゃうよねぇ…。
関数への引数時
定義側の規則はわかったのだが、関数に渡せる/渡せないの基準はよくわからなかった:
// 前の節の関数 func1~3 に対して、渡せる型は? |
buf1は定数文字列の配列で、上書き可能な文字列を要求するfunc2へは渡せない、これは順当。buf2は固定配列で、配列内容を書き換えるfunc1に渡せないのは妥当だけど、func3にも渡せない。- 渡せてもよさそうなに、なぜなのか!?
buf3は文字列と配列どちらも定数なので、当然func3にしか渡せない、順当。buf0はすべてに渡せてよさそうなのに、結果的にbuf2と同じ。なぜだ…。
非constポインタ型からconstポインタ型へは暗黙で変換できるもんかと思うんだけど、そういうわけでもないっぽい。 どういうルールなんだろう?
- Double pointer const-correctness warnings in C - Stack Overflow 読んでもよくわからなかった…
- その下にCの仕様がおかしい、C++はできる、とのこと。
確かに
g++で試してみるとbuf2は意図通りfunc3にも渡せるようになるが、buf0がfunc1に渡せないのは変わらず。