增量/减量运算符是否在Bash中未定义行为?

时间:2015-01-05 02:46:40

标签: c linux bash posix post-increment

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

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

在Bash中定义增量/减量运算符,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),似乎执行从左到右的评估,并在运算符{{1 }}出现。

2 个答案:

答案 0 :(得分:4)

查看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 */
        }

如您所见,使用C后增量实现后增量:

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

恕我直言,使用该代码,以及从左到右按令牌处理一行的事实,我可以说行为已经明确定义。

答案 1 :(得分:4)

你似乎正在寻找为bash找到一个明确的标准,就像C一样。不幸的是,没有一个。

实际上只有两个指南:

    开放组基本规范的
  1. Volume 3 (Shell and Utilities),通常称为" Posix",故意未明确规定。它确实说明算术评估等同于ISO C标准的第6.5节“表达式”中描述的算术评估"但是该标准没有为包含mutator和同一变量的另一种用法的表达式指定任何值(例如x + x++x++ + x++)。

  2. bash的特定版本的实际行为和手册,两者均不具备正式规范的条件,且任何标准组织均未以任何方式对其进行认证或祝福

  3. 因此,我不得不说没有文档定义像x++ + x++这样的表达式的算术评估的bash结果。即使当前bash版本的bash手册指定了它(它没有),并且即使可以从检查当前bash版本的源代码中推断出行为(它是,但不是必然很容易),这在任何意义上都不是正式规范。

    结果"未定义"在直观的意义上:没有定义结果。

    当然,没有任何法律要求每种编程语言都被完全指定,而许多编程语言则没有。实际上,虽然C和C ++都以ISO标准的形式享有详尽的定义,但标准故意没有定义许多用法,部分原因是这样做需要形式的错误检测,这会妨碍性能。这些决定已经并将继续引起争议,我无意偏袒任何一方。

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

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

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

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

    • 显式声明评估特定构造的结果是未定义

    前两个肯定是正式的规范,因为它们意味着评估的结果是明确定义的(在第二种情况下,定义应该/必须出现在实施手册中)。这样的构造绝对可以使用,当然特定于实现的构造将使程序不可移植。

    第三个并没有准确地定义它所描述的构造的价值,尽管它通常会提供一系列可能性。第四个是C / C ++专业,它指定构造无效,并且程序员有责任避免它,因为标准对实现强加无论什么。不应该使用这样的结构。

    取自特定规格的四个案例:

    • 错误检测。 (Java)"如果整数除法中除数的值为0,则抛出ArithmeticException。"

    • 具体执行的。 (Posix shell)"打开文件由以零开头的十进制数表示。 最大可能值是实现定义的;但是,所有实现都应支持至少0到9(包括0和9),以供应用程序使用。"

    • 未指明的行为。 (C / C ++)"未指定行为的一个例子是评估函数参数的顺序。" (来自C99标准的定义部分)。

    • 未定义的行为(C)"在两个操作[ / ]中,如果第二个操作数的值为零,则行为未定义。" (与上面的Java对比。)

    最后两个类别似乎在某些程序员中引发了一种愤怒;不相信规范可能会使行为没有明确规定,甚至缺乏规范也是隐藏真相的某种阴谋(因此必须将其设置为自由)。而这反过来导致随机实验与特定的语言实现,这必然是徒劳的,因为标准不会绑定所有实现做同样的事情,或甚至特定的实现,以始终如一地做同样的事情。

    避免考虑"未定义的行为"同样重要。作为一种特定的行为。如果使用UB进行的计算具有特定行为,则不会未定义。即使计算具有一系列可能的特定行为,也只是未指定。

    "未定义的行为"不是规范或属性。你无法检测到"未定义的行为"因为缺乏定义意味着任何行为都是可能的,包括行为,这是某些其他结构的定义结果。

    特别是"未定义的行为" 与检测到的错误相同,因为实现没有义务检测它。

相关问题