一个语言细节问题

这回还是一个语言细节问题:求值顺序,副作用等等。说白了和v[i]=i++是差不多的。不关心这类细枝末节的朋友们可以不用看了。

程序如下:

#include <iostream>

int
g( int i)
{
    return i;
}

int main()
{
    int i = 1 ;
    std::cout << i*g(i++);
    return 0 ;
}

起因是csdn上的一个 帖子。我本来认为这是一个和实现有关的问题,属于标准中未指定行为的那一类。在求值i*g(i++)时,左序和右序都是有可能的,结果分别为1和2。在我的Visual C++ 2005 Express上跑,结果是1,而g++ 3.4.2的结果为2。VC 2005相当狠,把i的自增一直排到了整个std::cout << i*g(i++);的后面!

而那个帖子里给的程序是这样的(一看感觉就很像那些计算机等级考试的鸟题目)

#include <iostream>

int f( int n)
{
    if (++n == 5 )
        return n++;
    return n*f(n++);
}

int main()
{
    std::cout << f( 1 );
    return 0 ;
}

歧义或者说问题也是n*f(n++)这一句。我拿Visual C++ 2005 Express和g++ 3.4.2分别跑了一下,结果是120(对应于左序)和300(右序)。但csdn上有人拿VS 2005 Team版和VC 6.0测试,结果都是300。打死我都不相信VS 2005 Team和Visual C++ 2005 Express的C++编译器会有什么差别。而且我尝试了好几个可能有影响的编译选项,例如优化,是否禁用语言扩展(/Za),以及release和debug,结果都是120。我机子上没有VS 2005 Team,所以没办法验证。谁能告诉我这到底是怎么一回事?

Update:终于找了一台有Visual Studio 2005 Team Suite的机器来验证上面的程序,和我的Express版运行结果完全相同。但是还是有不少朋友说他们测试的结果是300。此外,还有的是在debug下结果为300,而release下结果是120!简直乱套了。

结论:得归功于csdn网友ugg的反复测试。关键问题是Visual C++编译器的运行时检查选项。默认情况是/RTCs,即stack frame run-time error checking,此时运行结果是120;如果打开了/RTCu,msdn上的解释是Reports when a variable is used without having been initialized,那么结果就是300。可见,在没有打开/RTCu的时候,编译器把n++这个副作用放到了整个full-expression的后面,可能是因为编译器认为n++对表达式的求值没有影响。至于左序右序的问题,我仍然难以下结论。在打开了/RTCu的情况下,不管是n*f(n++)或f(n++)*n结果都是300,否则结果都是120。

我的想法是:编译器之所以敢这么优化(这并不算是太大的优化),前提就是这个求值顺序本来就是unspecified,编译器可以自由发挥。当然,左序右序的问题可能不是那么关键。这仍然是一个依赖于编译器实现的问题,而不是语法问题。

你可能感兴趣的:(一个语言细节问题)