Объявления/определения как утверждения в C и С++
Я был смущен, когда это не скомпилировалось в C:
int main()
{
for (int i = 0; i < 4; ++i)
int a = 5; // A dependent statement may not be declaration
return 0;
}
Я привык к C++, где это скомпилируется. Я просто уставился на какое-то время, пока не вспомнил ответ здесь о SO о том, как в C и C++ разные вещи считаются "заявлениями". Это было в отношении заявления о переключении. "Заявление" после скобок для цикла должно присутствовать как на C, так и на C++. Это можно сделать как для добавления точки с запятой, так и для создания блока {} squiggly bracket block.
В C++ "int a = 7;" считается объявлением, определением и инициализацией. В CI считают, что все это тоже считается, однако в C оно не считается "выражением".
Может кто-то точно разъяснить, почему в C это не утверждение, тогда как в C++ это так? Это путает мою концепцию того, что такое утверждение, потому что один язык говорит, что это так, а другой говорит, что это не так, поэтому я немного смущен.
Ответы
Ответ 1
В C++ утверждение есть (C++ 17 стандартный черновик)
excerpt from [gram.stmt]
statement:
labeled-statement
attribute-specifier-seqopt expression-statement
attribute-specifier-seqopt compound-statement
attribute-specifier-seqopt selection-statement
attribute-specifier-seqopt iteration-statement
attribute-specifier-seqopt jump-statement
declaration-statement
attribute-specifier-seqopt try-block
init-statement:
expression-statement
simple-declaration
declaration-statement:
block-declaration
...
Обратите внимание, что в C++ есть декларации, которые являются декларациями и являются операторами. Аналогично, простые объявления - это инструкции init. Однако не все объявления являются утверждениями. Грамматика деклараций содержит вещи, которые не входят в список утверждений:
excerpt from [gram.dcl]
declaration:
block-declaration
nodeclspec-function-declaration
function-definition
template-declaration
deduction-guide
explicit-instantiation
explicit-specialization
linkage-specification
namespace-definition
empty-declaration
attribute-declaration
block-declaration:
simple-declaration
asm-definition
namespace-alias-definition
using-declaration
using-directive
static_assert-declaration
alias-declaration
opaque-enum-declaration
simple-declaration:
decl-specifier-seq init-declarator-listopt ;
attribute-specifier-seq decl-specifier-seq init-declarator-list ;
attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt [ identifier-list ] initializer ;
...
Список грамматик декларации продолжается для нескольких страниц.
В C утверждение (стандартный проект C11)
excerpt from Statements and blocks
statement:
labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement
Обратите внимание, что нет объявлений, которые являются утверждениями в C.
Таким образом, значение выражения явно отличается на языках. Утверждение в C++, по-видимому, имеет более широкое значение, чем утверждение в C.
Ответ 2
C++ допускает, что "подкрепление" итерационного утверждения было неявным составным утверждением ([stmt.iter])
Если подстановка в итерационном заявлении представляет собой единый оператор, а не составной оператор, он как бы переписывается как составной оператор, содержащий исходный оператор. Пример:
while (--x >= 0)
int i;
могут быть эквивалентно переписаны как
while (--x >= 0) {
int i;
}
стандарт C не имеет этого языка.
Кроме того, определение оператора изменилось в C++ на включение заявления о декларации, поэтому даже если вышеуказанное изменение не было сделано, оно все равно будет законным.
Причина, по которой добавление фигурных скобок заставляет его работать, заключается в том, что теперь ваша декларация становится составной-заявкой, которая может включать объявления.
Вам разрешено иметь идентификатор в теле цикла без фигурных скобок, поэтому вы можете сделать это вместо этого:
int a = 5;
for (int i = 0; i < 4; ++i)
a;
Ответ 3
Согласно cppreference, C++ включает следующие типы statements
:
- выражения;
- составные заявления;
- выписки выбора;
- итерационные утверждения;
- подсказки;
- заявления о заявлениях;
- попробуйте блоки;
- атомные и синхронизированные блоки
Хотя C рассматривает следующие типы statements
:
- составные заявления
- выражения
- выписки выбора
- итерационные утверждения
- подсказки
Как вы можете заметить, декларации не считаются statements
на языке C, в то время как это не относится к C++.
Для C++:
int main()
{ // start of a compound statement
int n = 1; // declaration statement
n = n + 1; // expression statement
std::cout << "n = " << n << '\n'; // expression statement
return 0; // return statement
} // end of compound statement
Для C:
int main(void)
{ // start of a compound statement
int n = 1; // declaration (not a statement)
n = n+1; // expression statement
printf("n = %d\n", n); // expression statement
return 0; // return statement
} // end of compound statement, end of function body
Ответ 4
В C++ объявления - это утверждения, в то время как в объявлениях C нет утверждений. Итак, согласно грамматике C в этом цикле
for (int i = 0; i < 4; ++i)
int a = 5;
int a = 5; должно быть подпунктом цикла. Однако это декларация.
Вы можете сделать код, который будет скомпилирован в C, используя составной оператор, например,
for (int i = 0; i < 4; ++i)
{
int a = 5;
}
хотя компилятор может выдать диагностическое сообщение о том, что переменная a
не используется.
Еще одно следствие того, что в объявлениях C нет утверждений. Вы не можете помещать ярлык перед объявлением в C. Например, эта программа
#include <stdio.h>
int main(void)
{
int n = 2;
L1:
int x = n;
printf( "x == %d\n", x );
if ( --n ) goto L1;
return 0;
}
не компилируется в C, хотя он компилируется как программа C++. Однако, если поместить нулевой оператор после метки, программа скомпилируется.
#include <stdio.h>
int main(void)
{
int n = 2;
L1:;
int x = n;
printf( "x == %d\n", x );
if ( --n ) goto L1;
return 0;
}