【问题标题】:Declarations/definitions as statements in C and C++声明/定义作为 C 和 C++ 中的语句
【发布时间】:2018-09-26 11:13:49
【问题描述】:

当这不能在 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++ 中不同的东西是如何被视为“语句”的。这是关于 switch 语句的。在 C 和 C++ 中,for 循环括号后的“语句”必须存在。这可以通过添加分号或创建 { } 波浪形括号块来完成。

在 C++ 中“int a = 7;”被认为是声明、定义和初始化。在 C 中,我相信它也被认为是所有这些,但是在 C 中,它不被视为“语句”。

有人能准确解释一下为什么在 C 中这不是一个语句,而在 C++ 中它是一个语句吗?这使我对语句是什么的概念感到困惑,因为一种语言说它是,而另一种语言说它不是,所以我有点困惑。

【问题讨论】:

  • @NathanOliver 我首先在我的 Visual Studio 2017 上注意到了 C 和 C++ 编译器,但我知道这不是一个很好的合规性示例,所以我在 onlinegdb.com 上进行了尝试,两者都进行了编译,这是一个示例,onlinegdb.com/SysJyUGnf 抱歉,不确定这是使用 GCC 还是什么。但是如果你切换到右上角的 C++,你会看到两者之间的区别。
  • 您在寻找什么样的答案? C++ 委员会关于他们为什么决定做与 C 不同的事情的声明?
  • @melpomene 我开始明白这一点,一种语言中的一个定义不必在另一种语言中具有相同的定义,这就是为什么它是另一种语言的原因。只是在 C 和 C++ 中是如此相似,我希望像“语句”这样基本的东西是一个相似的概念,但这显然是一个很大的例外。我首先在一个非常流行的问题中注意到这一点,该问题是关于跳过 C 和 C++ switch 语句中的初始化。
  • C 有很多“方言”。最早的甚至根本不允许作用域变量。您必须在所有代码之前声明所有变量。例如,“K&R”C 就完全不同了。您应该说明您使用的是哪个版本的 C。 (C99 在现代编译器中最为常见)。

标签: c++ c if-statement language-lawyer


【解决方案1】:

C++ 允许迭代语句的“子语句”隐含地是复合语句 ([stmt.iter])

如果迭代语句中的子语句是单个语句而不是复合语句,则好像 它被重写为包含原始语句的复合语句。示例:

while (--x >= 0)
   int i;

可以等效地改写为

while (--x >= 0) {
   int i;
}

C 标准没有这种语言。

此外,statement 的定义在 C++ 中更改为包含 declaration statement,因此即使没有进行上述更改,它仍然是合法的。


添加大括号使其工作的原因是因为您的声明现在变成了一个可以包含声明的复合语句

你可以在没有大括号的循环体中有一个标识符,所以你可以这样做:

int a = 5;
for (int i = 0; i < 4; ++i)
    a;

【讨论】:

  • 我明白了。您的报价与迭代有关,但是 int a = 7;仍然不被接受为 switch 语句中的语句。它说:“标签只能是声明的一部分,而声明不是声明”。似乎并不只适用于迭代,尽管我可能会误解。
  • 关于迭代的引用(例如,for、while)也必须适用于案例标签,在 C++ 案例 1 中:int a = 7;我猜必须在 C++ 中将其视为复合语句?而在 C 中则不是。
  • @Zebrafish:这可能会有所帮助stackoverflow.com/q/92396/27678
【解决方案2】:

在 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 中没有声明是语句。


所以,statement 的含义在不同的语言中显然是不同的。 C++ 中的语句似乎比 C 中的语句具有更广泛的含义。

【讨论】:

    【解决方案3】:

    根据cppreference,C++包括以下类型的statements

    1. 表达式语句;
    2. 复合语句;
    3. 选择语句;
    4. 迭代语句;
    5. 跳转语句;
    6. 声明声明;
    7. 尝试块;
    8. 原子块和同步块

    虽然C 考虑以下类型的statements

    1. 复合语句
    2. 表达式语句
    3. 选择语句
    4. 迭代语句
    5. 跳转语句

    如您所见,声明在 C 中不被视为 statements,而在 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
    

    【讨论】:

    • 我看到 user2079303 并且您输入了相同的答案。这个和另一个在我看来准确地解释了正在发生的事情。谢谢。
    • 乐于助人 :) 是的,我发布并注意到他得到了相同的答案,时差不到一分钟,哈哈 ;)
    • 为了一致性,我也会注释 C++ 示例中函数定义的复合语句。
    【解决方案4】:

    在 C++ 中声明是语句,而在 C 中声明不是语句。所以根据这个for循环中的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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-03
      • 2018-09-09
      • 1970-01-01
      相关资源
      最近更新 更多