Я написал следующий код:
int tester(int n)
{
int arr[n];
// ...
}
Этот код скомпилирован, без предупреждений, с использованием g ++.
Мой вопрос — как? Параметр n известен только во время выполнения, в массиве он статически размещается. Как gcc компилирует это?
Это расширение, которое GCC предлагает для C ++, хотя массивы переменной длины («VLA») должным образом поддерживаются C начиная с C99.
Реализация не очень сложная; в типичной реализации стека вызовов функция должна только сохранить базу кадра стека и затем увеличить указатель стека на динамически заданную величину. VLA всегда сопровождаются предупреждением о том, что если число слишком велико, вы получаете неопределенное поведение (проявляющееся в переполнении стека), что делает их более хитрыми в использовании, чем, скажем, std::vector
,
В какой-то момент были предприняты попытки добавить аналогичную функцию в C ++, но это оказалось на удивление трудным с точки зрения системы типов (например, что такое тип arr
? Как это выводится в шаблонах функций?). Проблемы менее заметны в C, который имеет гораздо более простую систему типов и объектную модель (но, тем не менее, вы все еще можете утверждать, что C хуже из-за наличия VLA, значительная часть стандарта тратится на них, и язык будет было бы немного проще без них, и не обязательно беднее для этого).
Библиотека GNU C предоставляет функцию для выделения памяти в стеке — ALLOCA (3). Он просто уменьшает указатель стека, создавая на нем некоторое пространство для царапин. GCC использует alloca(3)
реализовать массивы переменной длины C99 — сначала он уменьшает указатель стека в прологе функции, чтобы создать пространство для всех автоматических переменных, размер которых известен во время компиляции, а затем использует alloca(3)
в дальнейшем уменьшить его и освободить место для arr
с размером, определенным во время выполнения. Оптимизатор может фактически объединить оба уменьшения.
int tester(int n)
{
int arr[n];
return 0;
}
компилируется в
;; Function tester (tester)
tester (int n)
{
int arr[0:D.1602D.1602] [value-expr: *arr.1];
int[0:D.1602D.1602] * arr.1;
long unsigned int D.1610D.1610;
int n.0;
...
<bb 2>:
n.0 = n;
...
D.1609D.1609 = (long unsigned int) n.0;
D.1610D.1610 = D.1609D.1609 * 4;
D.1612D.1612 = __builtin_alloca (D.1610D.1610); <----- arr is allocated here
arr.1 = (int[0:D.1602D.1602] *) D.1612D.1612;
...
Это эквивалентно следующему коду C:
int tester(int n)
{
int *arr = __builtin_alloca(n * sizeof(int));
return 0;
}
__builtin_alloca()
внутренняя реализация GCC alloca(3)
,