Объявить структуру в течение? - PullRequest
0 голосов
/ 25 ноября 2018

Apple LLVM 9.1.0 с clang-902.0.39.2, используя -std=c11, принимает:

typedef struct { int i; float f; } S;
for (S s = { 0, 0 }; s.i < 25; ++s.i, s.f = i/10.f)
    …

, но отклоняет:

for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
    …

с сообщением:

ошибка: объявление нелокальной переменной в цикле 'for'

Правильно ли Clang отклонить это, потому что это нарушает некоторое ограничение в стандарте C?Какой пункт и абзац?Или это ошибка Clang?

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

Резюме

Потенциальное нарушение стандарта C связано с этим предложением в C 2018 6.8.5 3:

Часть объявления оператора for должна декларировать толькоидентификаторы для объектов, имеющих класс хранения auto или register.

Поскольку struct { int i; float f; } объявляет как тип, так и идентификатор, возникает вопрос о том, как интерпретировать 6.8.5 3. Похоже,для меня это:

  • Вероятно, комитет намеревался запретить объявлять что-либо, кроме идентификаторов для auto или register объектов.
  • Этот случай использования, когда тип случайно объявленвозможно, не рассматривался.
  • Разрешение этого побочного заявления было бы безвредным и не слишком противоречащим намерению.

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

(в этом ответе я даю ссылки на стандарт С 2018, но язык устарел и существует вus версии, возможно, с другой нумерацией пунктов или абзацев.)

Объявлены как тип, так и идентификатор

Объявление в следующем выражении for объявляет оба идентификатора sи безымянный тип:

for (struct { int i; float f; } s = { 0, 0 }; s.i < 25; ++s.i, s.f = s.i/10.f)
    …

Мы знаем, что он объявляет тип, потому что C 2018 6.7.2.1 8 говорит:

Наличие списка struct-объявление-списка в структуре-or-union-спецификатор объявляет новый тип в единице перевода.

В соответствии с 6.7.2.1 1, struct { int i; float f; } является спецификатором структуры или объединения, а внутри него int i; float f; - список объявлений структуры.Итак, этот исходный код соответствует описанию 6.7.2.1 8, поэтому он объявляет тип.

Язык стандарта C неоднозначен

C 2018 6.8.5 3 говорит:

Часть объявления оператора for должна объявлять идентификаторы только для объектов, имеющих класс хранения auto или register.

Что касается грамматики и использования английского языка,для этого предложения возможны несколько значений, включая:

  1. Единственное, что декларация должна объявить, - это идентификаторы для объектов, имеющих класс хранения auto или register.
  2. Единственные идентификаторыобъявление должно объявлять идентификаторы для объектов, имеющих класс хранения auto или register.
  3. Единственные идентификаторы для объектов, которые декларация должна объявить, являются идентификаторами для объектов, имеющих класс хранения auto или register.

В первую очередь, проблема в том, что «только» не смежно с тем, что оно модифицирует.«Только» может быть изменение «идентификаторов» или «объектов» или «класса хранения». Можно предпочесть, чтобы модификатор модифицировал ближайшего к нему кандидата, но авторы предложений не всегда конструируют их таким образом.(Грамматически это может также изменить «наличие», таким образом квалифицируя объекты как имеющие только класс хранения auto или register и не имеющие ничего другого, например, не имеющие размера или других свойств. Мы легко исключаем это значение в семантическома не грамматические основания.)

Эти примеры иллюстрируют различия между значениями:

static int s                 // Prohibited by 1, 2, and 3.
extern int s(int)            // Prohibited by 1 and 2, permitted by 3.
struct { int i; float f; } s // Prohibited by 1, permitted by 2 and 3.
int s                        // Permitted by 1, 2, and 3.

Эффекты могут осветить намерение

Не похоже, что есть причина для предпочтения какого-либоиз этих значений, основанных на трудностях реализации C. Чтобы увидеть это, учтите, что реализация C может легко переписать:

for (declaration; …; …) …

в эквивалентный код:

{ declaration; for (; …; …) … }

Таким образом, еслиРеализация C может поддерживать объявления и операторы for в целом, она может поддерживать общие объявления в операторе for без значительных дополнительных усилий.

Какова цель 6.8.5 3?

Декларация вfor оператор обеспечивает удобство.Он обеспечивает хороший способ объявления некоторого итератора или другого объекта, используемого для управления циклом, при этом ограничивая область действия оператором for (что является преимуществом для избежания ошибок).Он не предоставляет никаких новых функций.Учитывая это, я ожидаю, что 6.8.5 3 было написано с целью дать возможность декларации служить этой цели, не раскрывая ее для других целей.Было бы странным, хотя и не невозможным, использовать любое из первых двух приведенных выше примеров в выражении for.

Если это так, я подозреваю, что поверхностное намерение комитета имело значение 1, но они это сделалине учитывать ситуацию, когда неназванный тип объявлен случайно.Когда мы размышляем над третьим примером, используя структуру, мы видим, что она необычна, но не слишком не соответствует общепринятому использованию выражения for:

  • Естественно, оно возникает какРешение проблемы, что только одна декларация может присутствовать в части объявления оператора for, но иногда полезно управлять циклом с несколькими объектами разных типов.
  • Это все еще объект савтоматическое создание хранилища, как предусмотрено для циклов for.
  • Технически объявленный тип не требуется вне цикла for.
0 голосов
/ 25 ноября 2018

C11:

6.8.5.3 Оператор for

1 Оператор

for ( clause-1 ; expression-2 ; expression-3 ) statement
ведет себя следующим образом: выражение expression-2 является управляющим выражением, которое вычисляется перед каждымвыполнение тела цикла.Выражение expression-3 оценивается как пустое выражение после каждого выполнения тела цикла.Если условие-1 является объявлением, область действия любых идентификаторов, которые оно объявляет, является оставшейся частью объявления и всего цикла, включая два других выражения;оно достигается в порядке выполнения до первой оценки управляющего выражения.Если предложение-1 является выражением, оно оценивается как пустое выражение перед первой оценкой управляющего выражения. 158 *

Упущено [2], просто говоря об обработке опущенных предложений / выражений...

158) Таким образом, пункт-1 определяет инициализацию для цикла, возможно, объявляя одну или несколько переменных для использования в цикле;управляющее выражение expression-2 определяет оценку, выполненную перед каждой итерацией, так что выполнение цикла продолжается до тех пор, пока выражение не сравнится равным 0;а выражение-3 определяет операцию (например, приращение), которая выполняется после каждой итерации.

Ни о каких ограничениях не упоминается.Так как второй пример является допустимым объявлением - и я предполагаю, что clang также не жалуется на следующее:

void f(void)
{
    struct { int x, y; } point;
    // use point...
}

- тогда второй пример должен быть действительным кодом C, так что, насколько я вижу, clangнеправильно в отклонении.

...