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
に渡せないのは変わらず。