这些语法正确的C ++语句是否具有任何意义?

时间:2014-04-10 01:31:28

标签: c++ syntax

在查看C ++语法时,我发现后缀大致定义如下:

Postfix ::=  Primary
        |    Postfix '['  Expression ']'
        |    Postfix '('  Expression ')'
        |    Postfix '.'  Identifier
        |    Postfix '->' Identifier
        |    Postfix '++'
        |    Postfix '--'

意味着foo.f++()在语法上是有效的 - 大概是因为函数是指针,它会引用已定义的下一个函数,但如果它在语义解析期间没有被捕获,我会感到震惊修改一个const对象 - 就像foo.f()<true>;一样似乎没有任何意义,而foo.++f()是不允许的,即使它或多或少是相同的作为第一个的东西。此外,定义了一元表达式,以便++*"hello world"在语法上有效,因为文字被认为与标识符的方式相同。

相反的是:

postfix0 ::= ScopeResolution
         |   postfix0 '.' postfix2
         |   postfix0 '->' postfix2

postfix1 ::= postfix0
         |   postfix1 '<' expression '>'

postfix2 ::= postfix1
         |   postfix2 '[' expression ']'
         |   postfix2 '(' expression ']'

postfix3 ::= postfix2
         |   Literal
         |   postfix3 '++'
         |   postfix3 '--'

在解析的句法阶段会出现捕获这样的无效表达式。起初我认为它只是遗留在标准中作为遗留物,但是Java和D等新语言也做同样的事情,因此这些语句带有某种意义导致语法被定义为这种方式?

3 个答案:

答案 0 :(得分:10)

C ++实际上并没有通过语法产生来定义。 BNF语法是作为语言规则的伴随提供的,以帮助理解,但语法错误语义错误之间没有,也不可能有区别。在C ++中,因为它没有无上下文语法。

因此,在将语法分析中“在语法分析期间捕获的语法有效”更改为“在解析的句法阶段捕获此类无效表达式”时,您尝试进行的改进完全没有意义,因为这些实际上并没有作为独立阶段存在。

C ++编译的各个阶段可以在标准的2.1节[lex.phases]中找到。该主题特别感兴趣的是第7阶段:

  

分隔标记的空白字符不再重要。每个预处理令牌都转换为令牌。 (2.7)。由此产生的标记在语法和语义上进行分析并翻译为翻译单元。

句法和语义分析是不可分割地一起进行的。语法错误 在解析的句法阶段(第7阶段)被捕获。

答案 1 :(得分:5)

另一个有趣的说明是,foo.f++()在语义上可以有效。但它与“下一个定义的函数”没有任何关系。

#include <iostream>

struct CallMe {
    void operator()() const
    { std::cout << "Used as function call.\n"; }
};

struct F_Type {
    CallMe operator++(int)
    { std::cout << "f was incremented.\n"; return {}; }
};

struct Foo_Type {
    F_Type f;
} foo;

int main()
{
    foo.f++();
}

输出:

f was incremented.
Used as function call.

答案 2 :(得分:2)

尝试使用gccg++编译并运行这个小程序:

#include <stdio.h>

void foo() {
    printf("foo()\n");
}

int main(void) {
    void (*bar)() = foo;
    bar++();
    bar();
}

如果解释为C代码,则没有编译器错误,字符串&#34; foo()\ n&#34;被打印两次,但是当它试图从foo()返回时会出现段错误,因为部分函数prolog似乎已被跳过。

所以,是的,至少gcc认为bar++()是有效的C代码,并且方便地废话。

<强>更新
正如zwol指出的那样(感谢),这是由于一个相当危险而非有用的gnu扩展,它处理指向void的指针,并作为指向大小为1的对象的指针,允许对它们进行指针运算。使用gcc --pedantic -Werror进行编译会产生预期的错误,gcc -std=c99没有。


g++不同的故事:这里我得到编译器错误

foo.c: In function ‘int main()’:
foo.c:9:5: error: ISO C++ forbids incrementing a pointer of type ‘void (*)()’ [-fpermissive]

这是C ++标准中的一项规定(第5.2.6节,正如Ben Voigt所指出的,谢谢):函数指针的指针算法未在C ++中定义。