Действителен ли goto в области действия функции alloca?

Стандарт C запрещает переход в область функций, где существует VLA.

VLA и вызов функции alloca должны иметь одинаковый результат на низком уровне.

(Я могу ошибаться, так как я всего лишь C, а не программист низкого уровня, но, на мой взгляд, это кажется остроумным)

Так будет ли следующий фрагмент также вести себя неопределенно?

int main()
{
    char *p;

    goto label1;

    {
        p = _alloca(1);
label1:
        p = NULL;
    }
}

Конечно, я не могу ссылаться на p, но как насчет поведения?


person dhein    schedule 23.05.2014    source источник
comment
Не в аббревиатурах - что такое VLA в этом контексте?   -  person cup    schedule 23.05.2014
comment
@cup В стандартной документации C это сокращение от Variable Length Array, что-то вроде char Array[variableLength];   -  person dhein    schedule 23.05.2014


Ответы (3)


Собственно, правило 6.8.6.1 гласит:

  A goto statement is not allowed to jump past any declarations of objects 
  with variably modified types.

В вашем коде не существует объекта с изменяемым типом. alloca не объявляет объект (о котором должен заботиться компилятор). Таким образом, нет ничего похожего на область действия для alloca и нет причин для неопределенного поведения в смысле правила 6.8.6.1.

ИЗМЕНИТЬ

Чтобы немного уточнить ответ: «неопределенность» поведения в случае VLA связана с обещанием объявления того, что объект «известен» в пределах его области (на уровне языка). Как правило, объявление устанавливает контекст для выполнения кода. Нет необходимости, чтобы он выполнялся во время выполнения. Однако это неверно в случае VLA: здесь это обещание частично реализуется во время выполнения, нарушая подход C к статическому объявлению. Чтобы избежать дальнейших конфликтов, которые могли бы привести к системе динамической типизации, правило 6.8.6.1 избегает таких конфликтов.

Напротив, на уровне языка alloca — это просто функция; его вызов не составляет никакой области действия. Он дает только обещание о своем поведении во время выполнения в случае его вызова. Если она не вызывается, мы ничего не «ожидаем» от функции. Таким образом, его чистое существование не вызывает никакого конфликта: оба случая (обход или не обход) имеют четко определенную семантику.

person Matthias    schedule 23.05.2014
comment
alloca является нестандартным, поэтому стандарт в любом случае ничего не говорит об этом. - person Potatoswatter; 23.05.2014
comment
@Potatoswatter: даже если он нестандартный, он просто ничего не декларирует. Таким образом, проблема с VLA здесь не актуальна. - person Matthias; 23.05.2014
comment
Я согласен, но концепции можно было бы сделать немного яснее. Текущая формулировка ответа, по-видимому, просто оценивает вызов alloca с точки зрения 6.8.6.1. - person Potatoswatter; 23.05.2014
comment
@Potatoswatter, но в любом случае это может вызвать неопределенное поведение, не так ли? - person dhein; 23.05.2014
comment
@Zaibis alloca идеально ведет себя так, как указано в конкретной документации вашего компилятора. Обычно он не взаимодействует с потоком управления или областью действия. Итак, вы должны быть в порядке. - person Potatoswatter; 23.05.2014

VLA и вызов функции alloca должны иметь одинаковый результат на низком уровне.

Есть еще несколько отличий. Объект VLA отбрасывается, когда область, в которой он объявлен, заканчивается, а объект памяти, выделенный alloca, отбрасывается при возврате функции.

Это имеет значение, потому что требование в c99, 6.8.6.1p1 ("Инструкция goto не должна переходить из-за пределов области действия идентификатора, имеющего изменяемый тип, внутрь области действия этого идентификатора") касается выделения/освобождения объектов во время выполнения с изменяемым типом. Здесь оператор alloca не выполняется после выполнения goto, поэтому я не думаю, что вызов goto вызовет неопределенное поведение.

person ouah    schedule 23.05.2014
comment
Аааа я даже не подумал о такой разнице. так что даже после выхода из области, где теоретически называется alloca, память будет refferencable, пока мы не достигнем возврата, не имеет значения, что мы оставили блочную область? А в других случаях, как вы думаете, может ли alloca вызвать UB в отношении правила goto другим способом? - person dhein; 23.05.2014
comment
@Zaibis, если метка находится после оператора alloca, как в вашем примере, функция alloca просто не вызывается. - person ouah; 23.05.2014

Стандарт C ничего не говорит о поведении alloca(). Некоторые компиляторы используют стек очень предсказуемым образом и обращаются к автоматическим переменным с помощью в значительной степени избыточного указателя кадра. В таких компиляторах можно зарезервировать место в стеке, просто вычитая значение из указателя стека, при этом компилятору не нужно знать или заботиться о рассматриваемом резервировании. Однако такой код будет плохо работать, если компилятор использует стек не так, как ожидало приложение.

Я не думаю, что что-то вроде:

  int *p = 0;
  if (!foo(1,2,3))
    goto skip_alloca;
  p=alloca(46);
skip_alloca:
  bar(4,5,6);
  ...

может быть более опасным, чем:

  int *p = 0;
  if (foo(1,2,3))
    p=alloca(46);
  bar(4,5,6);
  ...

Если в стеке нет остатков от вызова функции во время выполнения alloca(), любая операция, вероятно, будет безопасной. Если во время выделения в стеке есть остаток (например, потому что компилятор решил отложить очистку аргументов foo до вызова bar), это приведет к тому, что alloca() будет плохо себя вести. Использование версии кода goto может быть на самом деле безопаснее, чем версия с if, потому что компилятору будет сложнее определить, что отсрочка очистки от foo может быть выгодной.

person supercat    schedule 19.02.2018