在标准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 }}出现。
答案 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一样。不幸的是,没有一个。
实际上只有两个指南:
Volume 3 (Shell and Utilities),通常称为" Posix",故意未明确规定。它确实说明算术评估等同于ISO C标准的第6.5节“表达式”中描述的算术评估"但是该标准没有为包含mutator和同一变量的另一种用法的表达式指定任何值(例如x + x++
或x++ + x++
)。
bash的特定版本的实际行为和手册,两者均不具备正式规范的条件,且任何标准组织均未以任何方式对其进行认证或祝福
因此,我不得不说没有文档定义像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进行的计算具有特定行为,则不会未定义。即使计算具有一系列可能的特定行为,也只是未指定。
"未定义的行为"不是规范或属性。你无法检测到"未定义的行为"因为缺乏定义意味着任何行为都是可能的,包括行为,这是某些其他结构的定义结果。
特别是"未定义的行为" 不与检测到的错误相同,因为实现没有义务检测它。