if (1) int a = 2;
Эта строка кода является действительным кодом C++ (он компилируется как минимум), но недействительным кодом C (не компилируется). Я знаю, что между языками есть различия, но этот был неожиданным.
Я всегда думал, что грамматика
if (expr) statement
но это сделает его действительным в обоих случаях.
Мои вопросы:
Не могли бы вы прислать, какую ошибку вы получаете при компиляции, и, как сказал Lakeweb, какой компилятор вы используете
См. ответ Болова ниже для более значимой причины, почему существует разница.
Как указано в ответах ниже, это происходит всякий раз, когда ожидается утверждение, поэтому не ограничивается if. Это включает в себя «строку после» for, while, switch и ярлыки, и это лишь некоторые из них.
Еще одно место, которое показывает разницу, заключается в том, что вы не можете добавить метку к объявлению в C.
@JonathanLeffler, только что попробовал, и, кажется, я могу добавить метку или регистр перед объявлением в C99 и C11.
@KlaasvanAarsen: тестирование, особенно с GCC, неинформативно. §6.8.1 Помеченные операторы указывает, что помечать можно только операторы, а §6.8.2 Составной оператор указывает, что блочные операторы состоят из последовательностей объявлений и операторов. Грамматика стандарта C не допускает меток в объявлениях. С++ разрешает их; некоторые компиляторы C могут разрешить это, но это расширение по сравнению с (текущим) стандартным C. C2x может это изменить.
if (1) int a = 2; это просто объявляет и инициализирует переменную, которая сразу выходит за рамки?





Это тонкое и важное различие между C и C++. В C++ оператор Любые может быть заявление-декларация. В C нет такой вещи, как заявление-декларация; вместо этого декларация может появиться вместо утверждение в любом составное выражение.
Из грамматики C (спецификация C17):
compound-statement: "{" block-item-listopt "}"
block-item-list: block-item | block-item-listblock-item
block-item: declaration | statement
Из грамматики С++ (спецификация С++ 14):
compound-statement: "{" statement-seqopt "}"
statement-seq: statement | statement-seqstatement
statement: ... | declaration-statement | ...
Неясно, Почему существует ли эта разница, просто языки эволюционировали. Синтаксис C++ восходит к (как минимум) C++85. Синтаксис C был введен где-то между C89 и C99 (в C89 объявления должны были находиться в начале блока).
В исходных версиях C++ 85 и 89 область действия переменной, определенной в заявление-декларация, была "до конца объемлющего блокировать"). Таким образом, объявление в подобном if не сразу выйдет за пределы области действия (как это происходит в более поздних версиях), а вместо этого будет в области действия для операторов, следующих за if в той же области действия блока. Это может привести к проблемам с доступом к неинициализированным данным, когда условие ложно. Хуже того, если бы у var был нетривиальный деструктор, который вызывался бы, когда область видимости заканчивалась, даже если бы она никогда не была инициализирована! Я подозреваю, что попытка избежать подобных проблем привела к тому, что C принял другой синтаксис.
Imho, переход C на определение C++ упростил бы язык, удалив ошибки, в дополнение к удалению случайных различий. Просто позор, что они не сделали этого в C99, когда следовали общепринятой практике и позволяли смешивать утверждения и определения.
Это потому, что C и C++ по-разному определяют утверждение.
В C объявления не классифицируются как операторы. Составной оператор C состоит из открывающей {, списка опций блок-элемент и закрывающей }, где блок-элемент либо декларация, либо утверждение. (Это было изменено в C99, когда C добавил возможность смешивать объявления и операторы внутри блока.)
В C++ объявление классифицируется как оператор (но только если оно находится внутри составного оператора). Это позволяет упростить определение составное выражение: это {, за которым следует необязательный список утверждение, за которым следует }.
Разница не имеет большого практического эффекта; это всегда можно обойти. Одним из последствий является то, что в C++ допустимо, чтобы объявление сразу же следовало за меткой case, но в C это недопустимо.
В C декларация и утверждение являются разными объектами.
В C++ подмножество объявлений, называемое объявление блока, представляет собой тип оператора, в частности, это заявление-декларация. К ним относятся простые объявления, такие как int a=2.
ответ Криса (и другие) показывает, чем отличается грамматика.
Я хочу отметить, что if (1) int a = 2; не имеет смысла в C, но имеет смысл в C++. Поскольку у нас нет блока, а есть только 1 оператор/объявление, невозможно дальнейшее использование объявленной переменной (она сразу же выходит из области видимости). В C это не имеет смысла разрешать, но в C++ конструкторы и деструкторы могут иметь побочные эффекты, поэтому определение и инициализация переменной, которая немедленно выходит за пределы области видимости, может быть полезной и должна быть разрешена.
Однако адрес можно было использовать. int a=foo(&a);.
@tstanisl Но каков для этого возможный вариант использования? Все, о чем я могу думать, это если foo() требуется указатель на выходное местоположение, и вам не нужен этот результат.
@Barmar, да, но foo() может иметь некоторые побочные эффекты, и для выполнения этой работы может потребоваться внешнее хранилище. Хотя использование составного литерала foo(&(int){0}) может быть более подходящим решением.
Это одинаково полезно / бесполезно на любом языке, законно только на одном языке - это просто случайное различие.
@Deduplicator Хороший вопрос. То, что вы Можно используете переменную только для побочного эффекта конструктора/деструктора, не означает, что вы должен. Скрытие семантики делает код очень неясным.
@tstanisl Однако синтаксис foo(&(int){0}) полностью инициализирует объект перед передачей его адреса, в то время как синтаксис int a = foo(&a); не затрагивает память a до тех пор, пока foo() не вернется. Еще одно небольшое и тонкое отличие, которое могло изменить семантику программы.
@cmaster-reinstatemonica: Если вы когда-либо действительно хотели сделать это на C, тривиально обернуть {} объявление, которое включает вызов функции. Тогда это становится законным даже в C89. Тем не менее, это странная особенность грамматики, которая необходима, но на самом деле ее не нужно исправлять, поскольку всегда есть эквивалентный обходной путь, даже для целей оптимизации, в (надеюсь, во всех) реальных компиляторах.
@PeterCordes Я никогда не собирался так злоупотреблять C. Я просто отметил тот факт, что два варианта действительно демонстрируют различное поведение, т. е. что они не эквивалентны. Это не означает, что я когда-либо буду полагаться на эту разницу в поведении. В конце концов, код должен быть максимально простым.
Какой компилятор? В чем ошибка?