【问题标题】:Are C/C++ operator precedence & associativity rules ever violated?是否违反过 C/C++ 运算符优先级和关联性规则?
【发布时间】:2012-11-25 13:53:56
【问题描述】:

operator precedence & associativity rules 是否曾在任何 C/C++ 表达式中被违反?
如果有,能举个例子吗?

假设优先级和关联性规则的声明是:

每个运算符都有一个给定的优先级,每个优先级都有一个给定的关联性。如果两个运算符在它们期望操作数的地方看到一个子表达式,则它属于具有更高优先级的那个。联系被关联性打破了。

编辑:背景

该标准将 C/C++ 表达式定义为 CFG,它比基于优先级的解析器灵活得多。例如,可以给二元运算符提供不对称的“优先级”,这会导致 any 优先级表不正确。然而,在我看来,语法的设计被限制为支持简单的优先规则。以下是我遇到的一些所谓的“反例”:

1) a?b,c:d 不会被解释为(a?b),(c:d)

有人声称?: 运算符对其中间操作数的优先级与对其其他操作数的优先级不同,因为例如a?b,c:d 不会被解释为(a?b),(c:d)。但是,bc 都没有占据它在 ?: 看来作为其内部操作数的位置。按照这种推理,a[b+c] 应该被解释为(a[b)+(c]),这很可笑。

2) sizeof(int)*a 被解释为(sizeof(int))*a 而不是sizeof((int)(*a))

... 因为 C 不允许将无括号强制转换为 sizeof 的运算符。但是,这两种解释都符合优先规则。混淆来自* 运算符的歧义(是二元运算符还是一元运算符?)。优先表并不意味着解决运算符的歧义。毕竟,它们不是运算符-符号-优先表。所以 operator 优先规则本身是完整的。

3) a+b=c 导致语法错误,而不是语义错误

a+b=c,根据标准,是无效的 C 语法。如果 C 有一个基于优先级的解析器,它只会在 语义 级别被捕获。在 C 中,碰巧任何不是 unary-expression 的表达式都不能是左值的。因此,这些语义上注定要失败的 LHS 表达式不需要在语法上进行调整。它对整个语言没有影响,优先表不必用于预测表达式将导致的错误的句法/语义。

【问题讨论】:

  • 你为什么要问?这些规则应该是“具体的”,但总是存在编译器错误的可能性(尽管是远程的)。但更多情况下,这些规则只是被用户误解了。
  • @HotLicks - “优先规则”也可能是错误的。语言定义没有讨论优先级。
  • “问题是关于语言,而不是解析器”是什么意思?语法解析器要么正确反映语言规范,要么不符合标准。因此,任何符合语法的解析器都与该语言一致。如果不是符合标准的解析器,什么是“ 解析器”?
  • @HotLicks 我同意。查看添加的背景。
  • @SteveJessop 语法解析器。 C 采用了一些语法快捷方式,这些快捷方式会影响语法而不影响语言。我删除了那句话,并添加了示例 3。

标签: c++ syntax operators operator-precedence associativity


【解决方案1】:

例如,通常的优先级表表明 sizeof 和 cast 表达式具有相同的优先级。表格和标准都说它们是从右到左关联的。

当您查看*&foo 时,这种简化很好,这与*(&foo) 的含义相同。

它还可能向您建议 sizeof (int) 1 是合法的 C++,并且它与 sizeof( (int) 1 ) 的含义相同。但这不合法,因为实际上sizeof( type-id ) 在语法中是一个特殊的东西。它的存在阻止了sizeof (int) 1 成为一个sizeof 表达式,其操作数是一个转换表达式,其操作数是1

所以我认为您可以说 C++ 语法中的“sizeof (type-id)”产生式是通常优先级/关联性表所说的一个例外。他们确实准确地描述了“sizeof unary-expression”产生式。

【讨论】:

  • +1 发现有趣的东西!但正如您所建议的,有人可能会争辩说,sizeof(int) 中的sizeof(如int a=0 中的=)不是表中出现的运算符。毕竟,运算符优先级表不是词位优先级表。 (它甚至无法解决 operator 歧义。)
  • @Isak:好的,但如果问题是关于“遵守优先级和关联性”的语法的确切定义,那么我认为我对此不感兴趣。不能仅使用遵循表中规则的优先级解析器来解析 C 表达式,这一点很清楚。这是另一种说法,即存在 C 表达式的示例,其解析不是由表定义的。如果它们不是“例外”,那么我认为我没有理解这个问题。
  • 很明显sizeof(int)中的sizeof不是操作符,因为int不能是操作数。优先级表是关于 operators 的。 (还要注意,我的问题不是优先表施加的约束系统是否未确定,而是它们是否被违反过。即优先表是否真的是语言的属性。)
  • @Isak:在这种情况下,如果问题是,“是否有可能对 C++ 语法做出 1 个或多个真实的陈述,这些陈述用运算符优先级和关联性来表述?”那么我很确定答案是“是”。但是,您链接到的文章提供了一个示例,说明为什么这些表存在 FUD。 MSDN 将?: 列在其自身的优先级上,高于赋值运算符。文章指出,这对于true ? x : y = 5 是不准确的。如果所有其他方法都失败了,您可以省略可能导致混淆的运算符并获得准确但不完整的表格。
  • 如果你想把它排除在表之外,那么你只能说它不是一个运算符。无论哪种方式,您都在扩大标准对什么是和不是运算符的相当特别的方法:它将“sizeof(type-id)”列为“一元运算符”下的产生式,它没有列出任何一种@ 987654339@ 作为 unary-operator,并且在 5.3.3/1 中,C++03 明确指出 (int)sizeof(int)sizeof 的操作数,即使您在上面定义它不是一个操作数。基本上,您必须对标准中的术语有所限制才能生成该表。
【解决方案2】:

这取决于“规则”是否正确。语言定义没有讨论优先级,因此您在各个地方看到的优先级表可能反映也可能不反映语言定义实际需要的内容。

【讨论】:

  • 鉴于语言的定义方式,是否存在与标准完全不矛盾的优先表?查看添加的背景。
  • @IsakduPreez - 我听说优先级确实不能捕捉真正的规则,但我还没有研究过。这个标准对我来说已经足够清晰了,没有引入非官方的重新表述。
  • 如果已知 CFG 受优先规则约束,则有 大量 冗余信息进入 CFG 的定义中。对于大多数人来说,如果所有二元运算符的每一面都有不同的胶水,那将是一场噩梦。为什么?
【解决方案3】:

在有人能找到反例之前,我会提出这个作为默认答案:

不,C/C++ 优先级和关联性规则永远不会被违反。

【讨论】:

    猜你喜欢
    • 2013-10-02
    • 2012-08-13
    • 1970-01-01
    • 1970-01-01
    • 2014-10-22
    • 2021-05-05
    • 2020-10-20
    • 2021-03-29
    • 2013-07-31
    相关资源
    最近更新 更多