ポインタの定数ポインタ

定数宣言、ちゃんとやってますか?C/C++のマクロとかじゃ駄目ですよ、折角型チェックという素晴らしい機能があるのですから…。

さてさて、C++では定数の宣言が出来るようになったわけです(Cは出来なかった、、、と思う)。

  const char TEISU = 'T';    // 定数

こんなヤツです。そして、C/C++にはポインタと云う素晴らしい機能があるわけで、こいつも定数が宣言できます。

  const char* TEISU_NO_POINTER = "Hello Hoge!";

なんだけど、私はちと変な書き方を慣行していて、

  char const* TEISU_MO_POINTER2 = "Hello Hoge!";    // 上と等価

なんでそんな使い分けをしているのか?それは、皆さんご存知の通り、ポインタの定数には3つ種類があって、

  char const*      JITTAI_WO_SAWARENAI  = "Hello Hoge!";    // 実体を変更できないポインタ(const char* も等価)
  char      *const ADDRESS_WO_SAWARENAI = "Hello Hoge!";    // アドレスを変更できないポインタ
  char const*const NANIMO_SAWARENAI     = "Hello Hoge!";    // アドレスも実体も変更できないポインタ(const char*const も等価)

私は「アスタリスクの左にconstがあったら実体を触れない、右にあったらアドレスを触れない」と覚えるようにしているからです。アスタを境に右、左、って方が覚えやすくないすか?

そんでもって、C/C++には「ポインタのポインタ(のポインタ…)」ってえのもあるじゃないですか。胸が熱くなりますね。そうすっと、なんかイヤ〜な感じがしてきませんか…?

  char * const * KOREWA_NANI = argv;  // これはなんだ?

アスタリスクアスタリスクの間にあるconstはどっちに掛かるんだろう…。と気にすると泥沼にハマりそうだったので、今まで考えるのを避けてきたのです。が、今日、勇気を出して考えてみたよ!とりあえず、main()の2つめの引数、argv(と慣例で名づける)を設定する。で、実行ファイルに対してコマンドを

  ./a.out Hello_Hoge! Hello_Huga! Hello_Piyo!

と入力すると、

  argv[0] ... "./a.out"
  argv[1] ... "Hello_Hoge!"
  argv[2] ... "Hello_Huga!"
  argv[3] ... "Hello_Piyo!"

ってな感じになるのでしたね。そんで

  char * const * KOREWA_NANI = argv;

と代入します。この時、constが

  • 左のアスタに掛かる場合(アスタの右にconst):argvの実体を変更することが出来ない。argvの実体は"./a.out"へのポインタで、これを変更することが出来ない。
  • 右のアスタに掛かる場合(アスタの左にconst):"./a.out"へのポインタを変更することが出来ない。要はargvの実体が変更できない。

…てな感じで、まあ予想は出来たんですけど、「どっちでも一緒」でしたね。仕様を考えたひと、賢い〜〜〜天才だな〜。って私は感動しました。…それとも今更の常識事項だったのか???

まあ、定数ポインタって何に使うんだよ…って昔は疑問だったので例を挙げてみます。

  int Load( char const* FileName );

こんな感じでよく使いますね。Loadするのに渡されるファイル名はどーせ読み取り専用でいいのだから、普通はconst付けますね。そんで、後で使う時に「ポインタを渡すと、関数の中でぶっ壊されたりするんじゃないか…?」と無用な心配がインターフェースを見るだけで解消できます。独立性が上がるわけですな。

昔は、偏屈に

  int Load( char const*const FileName );

なんて書いてましたが、

  int Load( char const* FileName = 0)
  {
    if( !FileName )
      FileName = "DefaultFileName";

なんてやると便利だったりするし、そもそも、インターフェースを見たときに、ポインタの中身(実体)がぶっ壊されないことが保障されていれば、その後関数の中でアドレスの変更があろうが無かろうが使用者にとっては関係ないわけです。引数のアドレスを変更したくないのは実装者の都合なわけで。だもんで、*constのアドレス不変の定数ってあんまり使わないですね。

ということは、char const*とか書いてまで使い分ける必要も無かったということですね!