C++におけるsizeofの理解: なぜn8にならないのか?

C++で配列を扱う際、sizeof演算子に関連して予期しない結果に遭遇することがあります。特に、配列を関数に渡すときにはその傾向が顕著です。本記事では、関数のパラメーターとしてsizeofを使用する際に生じる一般的な混乱を探求し、特に以下の質問に答えます: なぜ関数foo()内のn8にならないのか?

問題

この問題を示す2つのコード例を見てみましょう。

例1: 関数foo()

void foo(char cvalue[8])
{
    int n = sizeof(cvalue);
}

上記の例では、sizeof(cvalue)を呼ぶと、n8と等しいことを期待するかもしれません。配列はサイズ8で定義されているからです。しかし、実際にはそうではありません。

例2: 関数bar()

void bar()
{
    char cvalue[8];
    int n = sizeof(cvalue);
}

この2つ目の例では、sizeof(cvalue)は確かに8に等しくなります。では、なぜ違いが生じるのでしょうか?

概念の理解

sizeof(cvalue)がそれぞれの関数で異なる値を返す理由を理解するためには、CとC++における配列の扱いについて明確にする必要があります。

関数パラメーターとしての配列

CやC++で配列を関数に渡すとき、実際には配列そのものを渡しているわけではありません。代わりに、渡されるのは配列の最初の要素へのポインタです。関数パラメーターで使用されるブラケットは単なる構文の記法であり、動作を変えるものではありません。以下の2つの宣言は同等です:

  • void foo(char cvalue[8])
  • void foo(char cvalue[])
  • void foo(char *cvalue)

これらの宣言すべてにおいて、cvalueはポインタとして解釈されます。したがって、foo()内でsizeof(cvalue)を呼び出すと、それは配列のサイズではなくポインタのサイズを返します。ほとんどのプラットフォームでは、このサイズは32ビットシステムで通常4バイト、64ビットシステムで通常8バイトであるため、foo()内のn8になりません。

bar()における正しいコンテキスト

対照的に、bar()内では、cvalueがサイズ8のローカル配列として定義されています。したがって、ここでsizeof(cvalue)が呼ばれると、配列全体のサイズが正確に反映され、n8になります。

重要なポイント

  • ポインタの動作を理解する: 配列をパラメータとして渡すと、実際にはポインタを渡しており、sizeofを使用すると誤解を招く値になることがあります。
  • ローカル配列: sizeofは関数内で定義されたローカル配列の実際のサイズを返し、期待される出力を提供します。
  • 構文糖: 関数のパラメーター内のブラケットは配列変数を作成するのではなく、ポインタを扱っていることを示しています。

結論

CとC++で配列を扱うことは難解であり、特に言語に不慣れな人にとってはそうです。ポインタと配列の違いを理解することは、正確な計算とデバッグには重要です。関数内では、配列と見なされているパラメータにsizeofを適用すると、意図した配列のサイズではなくポインタのサイズが返されることを覚えておいてください。

この説明が、関数内の配列引数に関連するsizeof演算子の動作についての理解を深め、なぜfoo()内でn8とならず、bar()内ではそうなるのかを明確にする手助けになれば幸いです。コーディングの際はこれらの原則を心に留めておいてください!