C Продолжительность хранения массивов переменной длины
На этом сайте есть следующий абзац (выделено мной):
- время автоматического хранения. Хранилище выделяется, когда блок, в котором был объявлен объект, вводится и освобождается, когда он завершается любыми средствами (goto, return, end). Одним из исключений является VLA; их хранение выделяется при выполнении декларации, а не в записи блока, и освобождается , когда декларация выходит за пределы области действия, а не после выхода блока (начиная с C99). Если блок вводится рекурсивно, для каждого уровня рекурсии выполняется новое распределение. Все функциональные параметры и объекты нестатического объекта блока имеют такую длительность хранения, а также составные литералы, используемые в объеме блока.
В чем разница между объявлением, выходящим из области видимости, и выводом блока? Можете ли вы привести пример?
Ответы
Ответ 1
Из проекта N1570 спецификации C11 §6.2.4/7
Для такого объекта, который имеет тип массива переменной длины, его время жизни продолжается от объявления объекта до выполнения программа оставляет область декларации.
Затем в этом описании добавляется эта полезная заметка:
Оставляя самый внутренний блок, содержащий объявление, или прыгающий точка в этом блоке или встроенный блок до объявления, оставляет область декларации.
Таким образом, VLA отменяется, когда выполнение выходит за пределы области VLA, которая включает раздел в том же блоке перед объявлением VLA.
Переход к точке до объявления может выполняться с помощью оператора goto
. Например:
int n = 0;
while (n < 5)
{
top:
n++;
char array[n];
if (n < 2)
goto top;
}
В этом коде блок не выходит, когда выполняется goto
. Однако значение n
изменяется, поэтому необходимо выделить новый array
. Это ужасно запутанная ситуация, которую спецификация пытается поддержать.
Ответ 2
В чем разница между объявлением, выходящим из области видимости, и выводом блока? Можете ли вы привести пример?
Область идентификатора области блока начинается с его объявления и продолжается до конца самого внутреннего блока. Это относится к идентификаторам любого типа, а не только к идентификаторам VLA. Возможно, что управление покинет область идентификатора без выхода из самого внутреннего блока, содержащего его, путем передачи в точку, предшествующую объявлению идентификатора. Самый очевидный способ выполнить это можно с помощью инструкции goto
:
int f(int n) {
// i is allocated here, _before_ the beginning of its scope
label: // if control returns here via the goto below, vla is _de_allocated
// at this point
printf("n is %d", n);
int vla[n]; // vla is (re)allocated here, at the beginning of its scope
int i;
int sum;
for (i = 0; i < n; i++) {
vla[i] = rand();
sum += vla[i];
}
if (sum < SOME_THRESHOLD) goto label;
return sum; // vla and i are both deallocated when control exits the block
}
Разница в том, когда VLA освобождается от того, когда обычный автоматический объект освобождается, отражается разница между тем, когда выделяются два типа объектов. VLA живут только в пределах своих идентификаторов.