C言語のconstとポインタはややこしい。
まずは普通の変数の宣言と代入です。
1 2 | int8_t test1 = 0; test1 = 1; |
これは普通のint8_tの変数を宣言し、
その後に1を代入しています。
次に、
1 2 | int8_t * test2 = &test1; *test2 = 1; |
これも普通のint8_tのポインタです。
test2にはtest1のアドレスを格納しているので、
*test2への代入はtest1への代入ということになります。
次は、
1 2 | int8_t ** test3 = &test2; **test3 = 1; |
これはポインタのポインタです。
test3にはtest2のポインタを格納しているので、
**test3への代入は*test2への代入、すなわちtest1への代入と同じことになります。
つまり、上記は3つとも、test1 = 1 という処理になります。
ここからはconstの登場です。
1 | const int8_t test4 = 0; |
これは定数としての宣言となります。test4の値は0となり、新たに値を代入しようとするとコンパイルエラーとなります。
次はポインタです。
1 | const int8_t * test5 = &test1; |
この場合、ポインタの指す先が定数ということになるため、*test5 = 1のようにしてtest1の値を書き換えることはできません。
test5 = &test4のように、test5の値を変更することはできます。
一方、次のような場合、
1 | int8_t * const test6 = &test1; |
*test6 = 1のようにしてtest1の値を書き換えることはできますが、test6 = NULLといったようにtest6の値を変更することができなくなります。
そして、下記のようにすると
1 | const int8_t * const test7 = &test1; |
*test7 = 1やtest7 = NULLなどどちらもできなくなります。
ややこしいです。さらにダブルポインタにしてみると余計にややこしくなります。
1 2 3 4 5 6 7 8 9 10 11 | int8_t test8 = 0; int8_t * test9 = &test8; int8_t ** test10 = &test9; int8_t ** const test11 = &test9; int8_t * const * test12 = &test9; int8_t * const * const test13 = &test9; const int8_t ** test14 = ( const int8_t **)&test9; const int8_t ** const test15 = ( const int8_t **)&test9; const int8_t * const * test16 = &test9; const int8_t * const * const test17 = &test9; |
test10は普通のダブルポインタの変数なので、
test10 = NULLや*test10 = NULL、**test10 = 1などいずれも可能です。
test11はtest11 = NULLがエラーとなりますが、*test11 = NULLや**test11 = 1は可能です。
以降も同じようにして、次のような結果となります。
test12 = NULL 可能
*test12 = NULL エラー
**test12 = 1 可能
test13 = NULL エラー
*test13 = NULL エラー
**test13 = 1 可能
test14 = NULL 可能
*test14 = NULL 可能
**test14 = 1 エラー
test15 = NULL エラー
*test15 = NULL 可能
**test15 = 1 エラー
test16 = NULL 可能
*test16 = NULL エラー
**test16 = 1 エラー
test17 = NULL エラー
*test17 = NULL エラー
**test17 = 1 エラー