这事情还没完呢……首先先看看标准。在ISO/IEC 9899:1999 C Standard 6.5.2.3里面有这么一句话:
10 The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.
那么,11L和大家仪仗的“函数参数由右向左计算”就不成立了(虽然我们知道,大多数情况下,函数参数的确是这么计算的)。
对于:z=fun(fun(x++,y++), fun(--x, --y));
为了区别,我们按照出现的顺序,称之为fun1,fun2,fun3。我们知道,fun1肯定在fun1和fun2调用之后才调用,这一点标准是严格规定了。而对于fun2来说,首先计算x++和y++,并返回x和y的原始值,接下来存在一个顺序点。而在fun3之中,也有相同的情况。问题在于,fun2和fun3的调用顺序,也就是上述两个顺序点的运算次序是“
unspecified”的。我们归纳如下:
假设先计算fun2的参数:
1 x为6,y为7,调用fun2,返回13,这时有一个顺序点,x变为7,y变为8
2 然后根据x=7,y=8调用fun3,这时有一个顺序点,x,y立即变为6,7然后调用fun3,这时返回13
3 fun1被调用,参数为13,13,结果应为26
假设先计算fun3参数:
1 x为6,y为7,立即自减以后调用fun3,参数为5,6,返回11。此顺序点后,x=5,y=6。
2 这时调用fun2,参数为5,6,返回11,顺序点后x,y自增为6,7。
3 调用fun1,参数为11,11,结果返回22。
所以,其实上面的说法不适合。确切的说,这道题的结果是“unspecified”而不是“Undefined”。其结果是有定义的,只不过并没有明确地由标准来确定,而是由实现来定义的。所以这段代码是不可移植的。
下面转一段《C专家编程》的内容:
ANSI C标准可以说是非常独特的,我们可以从好几个有趣的方面来说明这一点。它定义了下面一些术语,用于描述某种编译器的特点。如果你对这些术语有一个比较好的了解,就有助于你理解什么东西能被语言接受,什么东西不能被语言接受。前两个术语涉及不可移植的代码(Unportable code),接下来两个术语跟坏代码(bad code)有关。而最后两个术语则跟可移植的代码(portable code)有关。
[bo]不可移植的代码(unportable code)[/bo]
由编译器定义的(implementation-defined)——由编译器设计者决定采取何种行动(就是说,在不同的编译器中所采取的行为可能并不相同,但它们都是正确的),并作好文档记录,例如,当整型数向右移位的时候,要不要扩展符号位。
未确定的(unspecified)——在某些正确情况下的做法,标准未明确规定应该怎样做,例如:计算参数的顺序。
[bo]坏代码(bad code)[/bo]
未定义的(undefined)——在某些不正确情况下的做法,但标准并未规定应该怎样做,你可以采取任何行动,可以什么也不做,也可以发出一条警告信息,或者可以中止程序以及让CPU陷入瘫痪,甚至可以反射核导弹(只要你安装了能发射核导弹的硬件系统)。
例如:当一个有符号数溢出时应该采取什么行动。
约束条件(a constraint)——这是一个必须遵守的限制或要求。如果你不遵守,那么你的程序的行为就会变成像上面所说的那样是未定义的。这就出现了一种很有意思的情况:分辨某种东西是否是一个约束条件是很容易的,因为标准的每个主题都附有一个“约束(constraint)”小节,列出了所有的约束条件,现在又出现了一个更为有趣的情况:标准规定编译器只有在违反语法规则和约束条件的情况下才能产生错误信息!这意味着所有不属于约束条件的语义规则你都可以不遵循,而且由于这种行为属于未定义行为,编译器可以采取任何行动,甚至不必通知你!
突然发现自己好像在抄书……就当是练习打字吧……这是本好书,建议买了收藏……
[[it] 本帖最后由 StarWing83 于 2008-10-9 00:57 编辑 [/it]]