好的,我之前正在编写一些代码。具体是这一行:
EnterNode( FindNode( terrain_X, terrain_Y, travel_dir ), travel_dir );
我在测试程序后发现了一些奇怪的事情。外部函数接收的值不是我在检查堆栈时读取的值。
我做了一个示例程序: https://ideone.com/wNjJrE
#include <iostream>
using namespace std;
int modifyRef(int& A)
{
A = 0;
std::cout << "\nint& A = 0";
return A;
}
void TakeValues(int X, int Y)
{
std::cout << "\nX = " << X;
std::cout << "\nY = " << Y;
}
int main()
{
int Q = 9;
TakeValues(modifyRef(Q), Q);
std::cout << std::endl;
system("pause");
return 0;
}
这是我收到的输出:
int& A = 0
X = 0
Y = 9
这是一个错误吗?如何为参数绑定到函数调用定义操作顺序?
(道歉,如果我没有使用正确的术语,我主要是自学)
答案 0 :(得分:2)
函数参数的评估顺序是未指定。当你写:
TakeValues(modifyRef(Q), Q);
您依赖modifyRef(Q)
在Q
之前进行评估的事实。但是函数参数的评估顺序是未指定的 - 不一定是modifyRef(Q)
在Q
之前排序,反之亦然。
在这种情况下,首先评估Q
(第二个参数)。因此,我们阅读9
,并使用它初始化参数Y
。 然后我们评估modifyRef(Q)
,它将Q
归零并返回,这导致X
初始化为0
。
答案 1 :(得分:0)
尝试更改功能的签名:
int ModifyRef( int& A );
void TakeValues( int X, int Y );
以下......
int& ModifyRef( int& A );
void TakeValues( int& X, int& Y );
当你调用这行代码时,看看你的输出是什么:
int q = 9;
TakeValues( ModifyRef(q), q );
然后反转参数的顺序
TakeValues( q, ModifyRef(q) );
并比较结果。
当我在我的机器Win 7 64bit和Intel Core2 Quad Extreme上的VS2015社区上做这个时,我在两种情况下给出的结果是相同的,我得到的输出是:
为了测试目的,我将A
中ModifyRef()
的值从0更改为7。
int& A = 7
X = 7
Y = 7
对于内部函数是第一个参数而独立变量是第二个参数的情况,或者内部函数是第二个参数,其中独立变量作为第一个参数。
由于我收到相同的结果,在我看来,编译器如何创建和处理堆栈指针,ModifyRef()
似乎首先被评估,一旦该函数完成,因为q
是现在被引用而不是堆栈副本,它被在ModifyRef函数中设置的任何值覆盖。
我还稍微修改了你的ModifyRef()
函数,以便我可以看到它传入的参数是什么,而不是将数字硬编码到其打印输出语句中。
int& ModifyRef( int& A ) {
A = 7; // Changed Value From 0 To 7 For Testing Purposes
std::cout << "n\int& A = " << A;
return A;
}
但是,此效果可能仅适用于仅使用引用。
当我恢复原始功能签名并按此顺序调用时:
q = 9;
TakeValues( ModifyRef( q ), q );
正如您提供的原始代码一样,我得到的输出是:
int& A = 7
X = 7
Y = 9
然而,当我将参数反转为:
q = 9;
TakeValues( q, ModifyRef( q ) );
我的输出是:
int& A = 7
X = 7
Y = 7
所以我在这两种情况下看到的情况略有不同。
在参数的第一个顺序中,Y
或第二个参数设置为9,因为q
被初始化为9而Y
内的TakeValues()
正在打印出来值为9的堆栈副本。X
正在ModifyRef()
评估q
,其中TakeValues()
的值为9,但由于它是引用,因此它正在被修改,因此{{1}从X
设置q
,q
已经从9更改为7,因此X
现在设置为7。
在第二个参数顺序中,首先调用ModifyRef()
并将q
从9更改为7,因此TakeValues()
将Y
设置为7,因此函数使用引用q
也从9更改为7,因此当第一个参数q
设置X
q
时,已经从9更改为7。
我不知道这是否依赖于编译器,但至少在我的机器上看来参数的调用堆栈是从最右边到左边发生的。当您考虑由于函数具有默认值时,这也是有意义的。
示例:
class foo {
void bar( int a, int b, int c = 3 ) {
std::cout << a << ", " << b << ", " << c << std::endl;
}
};
函数声明中的所有默认值必须位于最右侧,因为您不能:
class foo {
void bar( int a = 1, int b, int c ) { ... } // Compile Error
};
我希望这有助于澄清当您开始在函数内调用函数作为参数时代码中发生了什么,如果您使用的是传值(堆栈副本)或引用。