execve のmanを読んでいるとき、 プロトタイプ宣言の中で「 char *const 」 と 「 const char * 」が混在して使われているのを見かけました。

int
execve(const char *path, char *const argv[], char *const envp[]);

pathは「 const char * 」で、argv と envp は「 char *const 」になっています。

調べてみたところ、const を置く位置によって、挙動が変わるとのこと。

「 const char * 」の場合は定数データ として扱われ、ポインタの参照先の内容を変更することができません。

一方、「 char *const 」は、定数ポインタ となり、別のポインタのアドレスを別の場所に変えることができなくなりますが、参照先の内容を変更することは可能です。

int main()
{
    /* const char * は「定数データ」 */
    const char *fixed_data = "this data can't change.";

    /* char *const は「定数ポインタ」 */
    char *const fixed_ptr = "this data can change.";

    char *another_str = "this is normal string.";

	fixed_data[0] = "T"; /* 定数データなので、内容を変更できない(コンパイルエラー) */
	fixed_data = another_str; /* ポインタは定数化されていないため、置き換えられる */

	fixed_ptr[0] = 'T'; /* 定数ポインタであり参照先は定数ではないので、内容の変更はできる */
	fixed_ptr = another_str; /* ポインタを変更することはできない(コンパイルエラー) */

  return (0);
}

上記のコードをコンパイルすると、こんな感じにエラーが出ます。

$ gcc test_const_variation.c
test_const_variation.c: In function ‘main’:
test_const_variation.c:11:16: error: assignment of read-only location ‘*fixed_data’
   11 |  fixed_data[0] = "T"; /* 定数データなので、内容を変更できない(コンパイルエラー) */
      |                ^
test_const_variation.c:15:12: error: assignment of read-only variable ‘fixed_ptr’
   15 |  fixed_ptr = another_str; /* ポインタを変更することはできない(コンパイルエラー) */
      |            ^

実にややこしいですね…良い覚え方はないものでしょうか。

とりあえずは、「*const」は定数ポインタ、と覚えておくことにしようと思います。

ウチイダは間接参照演算子(*)を変数名側に寄せる、いわゆる「ポインタ変数記法」を使うことが多いので、constに「*」がくっついたら、定数ポインタを定義しているように読むのが自然な気がしています。

以上、あなたのお役に立てればうれしいです。