Как размер массива переменной длины, вычисленный во время выполнения на C99?

В C89 длина массива известна во время компиляции. Но в C99 с массивами переменной длины длина массива может быть неизвестна до выполнения.

Итак, как он вычисляется?

И почему бы не вычислить длину динамически распределенного массива таким же образом?

Ответ 1

Из ИСО/МЭК 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
}

Ответ 2

Разница между массивом VLA и malloc ed, который вы удерживаете через переменную указателя (помимо жизни в разных частях памяти), заключается в том, что компилятор знает во время компиляции, что первым является массив. Он может хранить информацию о размерах (-ях) в каком-то месте вместе с VLA, поэтому в основном это своего рода скрытая переменная (ы). В зависимости от использования, которое вы используете с этой переменной, если вы используете с ним sizeof или если вы индексируете 2D-VLA через нечто вроде A[i][j], тогда компилятор может решить, действительно ли нужна эта скрытая переменная, а если нет, оптимизируйте его.