C89 では、配列の長さはコンパイル時に認識されます。しかし、可変長配列を使用する C99 では、実行前に配列の長さが不明な場合があります。
では、どのように計算されるのでしょうか。
また、動的に割り当てられた配列の長さを同じ方法で計算できないのはなぜですか?
C89 では、配列の長さはコンパイル時に認識されます。しかし、可変長配列を使用する C99 では、実行前に配列の長さが不明な場合があります。
では、どのように計算されるのでしょうか。
また、動的に割り当てられた配列の長さを同じ方法で計算できないのはなぜですか?
VLAとmalloc
ポインタ変数を介して保持するed配列(メモリのさまざまな部分に存在する以外)の違いは、コンパイラがコンパイル時に最初の配列が配列であることを認識していることです。サイズ情報をVLAとともにどこかに保持できるため、基本的にこれはある種の隠れた変数です。その変数で行う使用法に応じて、たとえばそれを使用sizeof
する場合、またはコンパイラなどを介して2D VLAにインデックスを付ける場合A[i][j]
は、その隠れた変数が本当に必要かどうかを判断し、必要でない場合は最適化します。
ISO/IEC 9899:TC3 セクション 6.7.5.2: 配列宣言子より
可変的に変更された型を持つ通常の識別子 (6.2.3 で定義されている) は、ブロック スコープとリンケージなし、または関数プロトタイプ スコープのいずれかを持たなければなりません。識別子が静的ストレージ期間を持つオブジェクトであると宣言されている場合、可変長配列型を持たないものとします。
VLA のサイズは単純にsizeof(vla_element_type) * vla_length
. VLA はブロック内でのみ定義できるためits length must be either a local variable or a function parameter
、vla にアクセスするときにコンパイラがアクセスできます。(vla の長さと vla 自体が同じスタック フレームに属しているため)。
Here is an example:
int main(int argc, char* argv[])
{
int m;
scanf("%d\n", &m);
int a[m];
printf("%d\n", sizeof(a));
return 0;
}
でコンパイルするとclang -o test.ll -O2 -emit-llvm -S test.c
、生成された IR は次のようになります。
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
entry:
// Allocate space on stack for m
%m = alloca i32, align 4
// call scanf
%call = call i32 (i8*, ...)* @__isoc99_scanf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32* %m) nounwind
// %0 now contains the value of m
%0 = load i32* %m, align 4, !tbaa !0
// %1 is m << 2, which is m * sizeof(int)
%1 = shl nuw i32 %0, 2
// call printf, output m * sizeof(int) to screen.
%call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %1) nounwind
// DONE.
ret i32 0
}