C言語やC++のsizeof式は、オペランドの型情報を元にオペランドの評価結果のサイズをバイト数で返します。評価結果の値がどうなるかは関係ありません。C++の型には静的な型と動的な型(ポインタまたは参照が実際に指している多相オブジェクトの型)がありますが、sizeof式は必ず静的な型のサイズを返します。
1998年にC++の標準規格が出来たころまでは、(処理系の独自拡張を除き)sizeof式の評価結果は必ず定数式になると考えて問題ありませんでした。しかし、C99が登場してからはそうはいかなくなりました。C99には可変長配列があります。可変長配列は次のように使います。
0 1 2 3 4 5 6 |
void func(int n) { int array[n]; ... } |
この func関数の中でsizeof arrayと書けば結果はどうなるでしょうか? 意味的に考えて、このsizeof式は sizeof(int) * nに評価されることでしょう。しかし、nは定数式ではなく、func 関数が実際に呼び出されるまで決定できません。
C++に限れば、C++20に至るまで(処理系の独自拡張を除けば)sizeof式は必ず定数式になると考えて問題ありません。また、マイコン向けの処理系には、C99対応と謳っていても可変長配列に対応していないものも実在しています。
実用上の問題としては、C言語の場合、switch文のラベルに sizeof式を使った式を記述できないことぐらいしか考えられません。C90では集成体(配列と構造体)の初期化子には定数式しか含めることができませんでしたが、C99 ではこの制約は静的記憶域期間を持つ場合に限られるため実質的な問題はありません。あと、関数の中で列挙体を定義した場合に、定数式でなければ列挙子の値に使うことができないという問題がありますが、実際に遭遇することは少ないでしょう。
ところで、GCCのようにtypeof演算子が使える処理系では、可変長配列を使った場合にはtypeof式もある意味動的に解決されることになります。次のようなコードで簡単にエラーになってしまうので要注意です。おそらく、こちらはsizeof式より罠にはまる確率が高そうです。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template <typename T> class A { }; int n; int main() { int array[n]; A<typeof(array)> a; return 0; } |
なお、C99で導入された可変長配列ですが、次の規格であるC11では任意実装になりました。__STDC_NO_VLA__マクロが定義されている場合は、その処理系は可変長配列に対応していません。