Объявления/определения как утверждения в 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:

  1. выражения;
  2. составные заявления;
  3. выписки выбора;
  4. итерационные утверждения;
  5. подсказки;
  6. заявления о заявлениях;
  7. попробуйте блоки;
  8. атомные и синхронизированные блоки

Хотя C рассматривает следующие типы statements:

  1. составные заявления
  2. выражения
  3. выписки выбора
  4. итерационные утверждения
  5. подсказки

Как вы можете заметить, декларации не считаются 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;
}