这些语法正确的C++语句有任何意义吗

Do these syntactically correct C++ statements carry any meaning?

本文关键字:语句 任何意 C++ 语法      更新时间:2023-10-16

当浏览C++语法时,我发现后缀的定义大致如下:

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

这意味着foo.f++()在语法上是有效的——大概是因为函数是指针,它会引用定义的下一个函数,但如果它在语义解析过程中没有被发现是在修改常量对象,我会感到震惊——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 '--'

在解析的语法阶段会出现catch这样的无效表达式。起初,我认为它只是作为遗留的东西留在标准中,但Java和D等较新的语言也做同样的事情,那么这些语句是否具有某种意义,从而导致语法以这种方式定义?

C++实际上并不是由其语法产物定义的。BNF语法是作为语言规则的辅助来帮助理解的,但C++中的语法错误语义错误

因此,您试图将"语法有效…在语义解析过程中捕获"更改为"在解析的语法阶段捕获此类无效表达式"的改进是完全没有意义的,因为这些实际上并不作为独立阶段存在。

C++编译阶段见本标准[lex.phases]第2.1节。这个主题特别感兴趣的是第7阶段:

分隔标记的空白字符不再重要。每个预处理令牌都被转换为一个令牌。(2.7)对生成的令牌进行语法和语义分析,并将其翻译为翻译单元。

句法分析和语义分析是密不可分的。语义错误是在解析的句法阶段,即第7阶段捕获的

另一个有趣的注意事项是,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.

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

#include <stdio.h>
void foo() {
    printf("foo()n");
}
int main(void) {
    void (*bar)() = foo;
    bar++();
    bar();
}

如果解释为C代码,则没有编译器错误,字符串"foo()\n"会打印两次,但当它试图从foo()返回时会出现segfault,因为函数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++中没有定义。

相关文章: