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 живут только в пределах своих идентификаторов.