Call by value

call by value是指调用函数时,为函数传递的参数是变量的copy,也就是说,函数内部对此参数进行的任何操作在函数外都是不可见,比如:

int inc(int n)
{
    printf("address of n: %x\n", (unsigned int)(&n));
    n++;
    printf("value of n: %d\n", n);
    return n;
}

int main()
{
    int m = 0;
    printf("address of m: %x\n", (unsigned int)(&m));
    inc(m);
    printf("value of m: %d\n", m);
    return 0;
}
// output:
//   address of m: ffffcc0c
//   address of n: ffffcbe0
//   value of n: 1
//   value of m: 0

main函数中调用inc时,为inc传递了变量m作为参数,但其实在inc函数内部,nm的一个copy,对n的任何操作,都不会影响m的值。

C语言的传参方式为call by value,如果想让函数内对参数的操作影响函数外的变量,就需要使用指针。

int *inc(int *n)
{
    printf("value of n: %x\n", (unsigned int)n);
    (*n)++;
    printf("value of *n: %d\n", (*n));
    return n;
}

int main()
{
    int m = 0;
    printf("address of m: %x\n", (unsigned int)&m);
    inc(&m);
    printf("value of m: %d\n", m);
    return 0;
}
// output:
//   address of m: ffffcc0c
//   value of n: ffffcc0c
//   value of *n: 1
//   value of m: 1

在上面的代码中,将变量m的地址值的copy传递给n,那么对n指向的内容进行操作,就相当于对变量m进行了操作。

Call By Reference

call by reference是指调用函数时,传递给函数的参数是变量的reference,而不是变量的copy,这意味着函数可以操作作为参数的变量,而且这些修改对调用者是可见的。

Call By Sharing

call by sharing暗示着这种语言中的值是基于对象而不是原始值,也就是说,所有的值都是一种对象,有自己的属性或方法。

1. 以mutable object作为参数

如果作为参数传给函数的变量是可变的,那么在函数中对这个参数的更改对调用者来说是可见的。这是因为在向函数传参时,函数体中的形参被绑定到传给函数的参数所绑定的对象上,这时这个对象是被共享的。

def append_1(l):
    print("id of l: ", id(l))  # id(l) == id(m)
    l.append(1)
    print("id of l: ", id(l))  # id(l) == id(m)

m = []
print("id of m: ", id(m))
append_1(m)
print("value of m: ", m)  # [1]

# output:
#   id of m:  1393538893832
#   id of l:  1393538893832
#   id of l:  1393538893832
#   value of m:  [1]

在以上代码中,当函数被调用时,变量l与变量m绑定到同一个对象上(id(l) == id(m)),所以对l的修改会影响到m

但是在函数体中对其形参进行赋值时,这个操作对调用者是不可见的。这是因为赋值操作会把这个形参绑定到另一个对象上,而不是改变这个原有对象。而对调用者来说,变量的绑定没有改变。

def asg_1(l):
    print("id of l: ", id(l))  # id(l) == id(m)
    l = [1]
    print("id of l: ", id(l))  # id(l) != id(m)

m = []
print("id of m: ", id(m))
asg_1(m)
print("value of m: ", m)  # []

# output:
#  id of m:  1393539676232
#  id of l:  1393539676232
#  id of l:  1393539049672
#  value of m:  []

在以上代码中,因为asg_1内存在赋值操作l = [1],导致变量l重新绑定到另一个对象上,而m变量所绑定的对象并没有改变。

2. 以immutable object作为参数

对于不可变对象,在call by sharing与call by value之间没有真正的不同。

3. JavaScript和Python中的对象

JavaScript和Python的参数传递方式都是call by sharing,所以要判断函数是否会对参数造成调用者可见的影响时,只要根据传入参数是否为可变对象即可。以下是JavaScript和Python中的mutable object和immutable object:

  JavaScript Python
mutable
object
Object(Function,
Date, Array,
RegExp)
dictionary,
list
immutable
object
Boolean, Null,
Undefined, Number,
Symbol, String
tuple,
number,
string

参考:call by sharing