1
2
3
4
5
6
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}

有多少人写过这样的交换函数,然后发现调用后并不能交换。老师告诉我们因为这是按值调用。

按值调用是求值策略中的一种。

求值策略是一种表达式求值时的时机(严格求值,非严格求值等)和求值的内容(传值,传引用,传指针,传名等)的策略。

严格求值

严格求值是指参数总在函数调用前求值。也就是指明了求值策略的时机

根据求值的内容划分,常见的形式有按值调用按引用调用

按值调用(call by value),参数传递的是值的副本,不能改变原本的值。实现方式有值传递(pass by value)

按引用调用(call by reference),参数传递的是引用的副本,也就是关注的值的引用。能改变关注的值。实现方式有传引用(pass by reference),传指针(pass by pointer)等。

所以要从行为上区分,如果只从数据上看,所有都是按值调用,区分求值策略也就没有意义了。

求值策略跟语言语法无关,实现方式才跟语言语法有关。

例子

以 C ++ 为例

1
2
3
4
5
6
7
8
9
void plusOne(int second)
{
second = second + 1;
}

void plusOneByReference(int& second)
{
second = second + 1;
}

调用

1
2
3
4
5
6
7
8
int main()
{
int a = 0;
int b = 0;
plusOne(a); // 按值调用
plusOneByReference(b); // 按引用调用
return 0;
}

plusOne 调用,关注的值 a 没法变化。所以是按值调用。

plusOneByReference 调用,关注的值 b 可以变化,所以是按引用调用。

在 C++ 里并不能通过只看函数签名就能得出求值策略。也就是同一个函数可能有不同的求值策略

1
2
3
4
void minusOne(int* second)
{
*second = *second - 1;
}

调用

1
2
3
4
5
6
7
8
int main()
{
int c = 0;
int* d = &c;
minusOne(&c); // 按引用调用
minusOne(d); // 按值调用
return 0;
}

第一个 minusOne 调用,关注的值 c 可以变化,所以是按引用调用。

第二个 minusOne 调用,关注的值 d 没法变化,所以是传值调用。

所以一个函数不能直接从签名上得出求值策略。需要从行为上考虑。

最后

还有按名调用(call by name)和 按未来调用(call by future )我懒得说了(逃

参考链接 传值还是传引用