C言語のconstとポインタはややこしい。
まずは普通の変数の宣言と代入です。
int8_t test1 = 0; test1 = 1;
これは普通のint8_tの変数を宣言し、
その後に1を代入しています。
次に、
int8_t* test2 = &test1; *test2 = 1;
これも普通のint8_tのポインタです。
test2にはtest1のアドレスを格納しているので、
*test2への代入はtest1への代入ということになります。
次は、
int8_t** test3 = &test2; **test3 = 1;
これはポインタのポインタです。
test3にはtest2のポインタを格納しているので、
**test3への代入は*test2への代入、すなわちtest1への代入と同じことになります。
つまり、上記は3つとも、test1 = 1 という処理になります。
ここからはconstの登場です。
const int8_t test4 = 0;
これは定数としての宣言となります。test4の値は0となり、新たに値を代入しようとするとコンパイルエラーとなります。
次はポインタです。
const int8_t* test5 = &test1;
この場合、ポインタの指す先が定数ということになるため、*test5 = 1のようにしてtest1の値を書き換えることはできません。
test5 = &test4のように、test5の値を変更することはできます。
一方、次のような場合、
int8_t* const test6 = &test1;
*test6 = 1のようにしてtest1の値を書き換えることはできますが、test6 = NULLといったようにtest6の値を変更することができなくなります。
そして、下記のようにすると
const int8_t* const test7 = &test1;
*test7 = 1やtest7 = NULLなどどちらもできなくなります。
ややこしいです。さらにダブルポインタにしてみると余計にややこしくなります。
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 エラー