increment/decrement operators 是 Bash 中的未定义行为吗?

Is increment/decrement operators Undefined Behaviour in Bash?

在标准 C99 和 C11 中,如下所示的表达式是 U.B。 (未定义的行为):

 int x = 2;
 int ans = x++  +  x++;  

在Bash中定义了increment/decrement运算符,gnu.org中的官方文档说遵循了标准C的约定。

此外,Bash 大部分符合 POSIX 并且在其标准文档 (http://pubs.opengroup.org/onlinepubs/9699919799/) 中表示,对于算术运算,除非另有说明,否则假定为 C 标准.

由于找不到更多信息,我的结论是在 Bash 中我们也有带有增量运算符的未定义行为:

x = 2
echo $(( x++ + x++ ))

我需要确定我的结论是否正确,或者相反,Bash 中是否存在某些取代 C 标准的约定。

附加说明:在我的系统中尝试(Ubuntu 14.04,Bash 4.3.11 版)似乎执行了从左到右的评估,增量为在运算符 ++ 出现的地方立即完成。

查看 bash 代码 (bash 4.3),来源 expr.c,我看到以下内容:

      /* post-increment or post-decrement */
      if (stok == POSTINC || stok == POSTDEC)
        {
          /* restore certain portions of EC */
          tokstr = ec.tokstr;
          noeval = ec.noeval;
          curlval = ec.lval;
          lasttok = STR;    /* ec.curtok */

          v2 = val + ((stok == POSTINC) ? 1 : -1);
          vincdec = itos (v2);
          if (noeval == 0)
        {
#if defined (ARRAY_VARS)
          if (curlval.ind != -1)
            expr_bind_array_element (curlval.tokstr, curlval.ind, vincdec);
          else
#endif
            expr_bind_variable (tokstr, vincdec);
        }
          free (vincdec);
          curtok = NUM; /* make sure x++=7 is flagged as an error */
        }

如您所见,post 增量未使用 C post-增量实现:

v2 = val + ((stok == POSTINC) ? 1 : -1);

恕我直言,使用该代码,以及从左到右逐个标记处理一行的事实,我可以说行为定义明确。

您似乎正在寻求为 bash 找到定义的标准,就像 C 一样。不幸的是,没有。

真的只有两个指南:

    Open Group Base Specifications 的
  1. Volume 3 (Shell and Utilities),俗称"Posix",故意未指定。它确实声明了算术评估 "be equivalent to that described in Section 6.5, Expressions, of the ISO C standard,",但该标准没有为包含增变符和同一变量的另一种使用(例如 x + x++x++ + x++)的表达式指定任何值。

  2. bash特定版本的实际行为和手册,两者都不符合正式规范,也都不是以任何方式获得任何标准组织的认证或认可。

因此,我不得不说,没有文档定义 bash 中对 x++ + x++ 等表达式的算术求值结果。即使当前 bash 版本的 bash 手册指定了它(它没有),即使有可能从当前 [=124] 的源代码检查中推断出行为=] 版本(确实如此,但不一定容易),从任何意义上讲都不是正式规范。

这使得结果 "undefined" 在单词的直观意义上:没有定义结果。

当然没有法律要求完全指定每种编程语言,而且很多都没有。事实上,虽然 C 和 C++ 都享有 ISO 标准形式的详尽定义,但标准有意未定义许多用法,部分原因是这样做需要各种形式的错误检测,这会影响性能。这些决定一直存在并将继续存在争议,我无意偏袒任何一方。

我会简单地观察一下,在正式规范的上下文中,以下是相同的:

  • 要求对特定程序构造的评估结果被检测为错误

  • 明确声明特定构造的值是实现定义的

  • 未能指定特定构造的值,或未指定的明确声明。

  • 明确声明对特定构造求值的结果是未定义

前两个绝对是正式规范,因为它们暗示评估结果是明确定义的(在第二种情况下,定义 should/must 出现在实施手册中)。这样的构造肯定是可用的,尽管特定于实现的构造当然会使程序不可移植。

第三个没有准确定义它所描述的构造的值,尽管它通常会提供一个可能性列表。第四个是 C/C++ 特长,指定构造无效,程序员有责任避免它,因为标准对 没有任何要求一个实现。永远不应使用此类结构。

具体规范中的四种情况示例:

  • 错误检测。 (Java)"if the value of the divisor in an integer division is 0, then an ArithmeticException is thrown."

  • 特定于实现。 (Posix shell) "Open files are represented by decimal numbers starting with zero. The largest possible value is implementation-defined; however, all implementations shall support at least 0 to 9, inclusive, for use by the application."

  • 未指定的行为。 (C/C++) "An example of unspecified behavior is the order in which the arguments to a function are evaluated."(来自 C99 标准的定义部分)。

  • 未定义的行为 (C) "In both operations [/ and %], if the value of the second operand is zero, the behavior is undefined."(与上面的 Java 对比。)

最后两个类别似乎在某些程序员中引发了一种愤怒;不相信规范可能会留下未指定的行为,甚至没有规范是某种隐藏真相的阴谋(因此必须释放)。这反过来又会导致对特定语言实现的随机实验,这肯定是徒劳的,因为标准不会绑定所有实现来做同样的事情,甚至不会让特定的实现始终如一地做同样的事情。

避免将 "undefined behaviour" 视为特定行为也很重要。如果使用 UB 的计算具有特定行为,则它不会是 undefined。即使计算有一系列可能的特定行为,也只是未指定。

"Undefined behaviour" 不是规范或属性。您无法检测到 "undefined behaviour",因为缺少定义意味着任何行为都是可能的,包括作为其他构造的定义结果的行为。

特别是,"undefined behaviour" 与检测到的错误不同,因为实施没有义务检测它。