不要再TMD讨论值传递和引用传递了

不要再TMD讨论值传递和引用传递了

网上有太多这样的无效讨论了,像「Java 是值传递还是引用传递?」,「Python 传可变对象就是引用传递,传不可变对象就是值传递!」、「一切都是值传递!」的话题真的是起码一个月来一次。

什么是值传递和引用传递?

传引用调用、传值调用是计算机科学里面的求值策略。求值策略定义何时和以何种次序求值给函数的实际参数,什么时候把它们代换入函数,和代换以何种形式发生。

  • 值传递意味着传 作为参数。
  • 引用传递意味着传 变量 作为参数。

注意这里的引用传递,它和引用类型毫无关系,更和 Java 和 Python 的引用类型没有关系( Java 之类的引用类型更像是 C/C++ 里面的指针)。C++ 里面的类似别名一样的引用类型,和 Java 、Python 的引用类型不同,它有时候在传递过程中符合引用调用的特点。

函数调用

要清楚为什么,就得先明白是什么。

以 C++ 为例:

void func(int x){
    x++;
}

int main(){
    int test = 2;
    func(test);
    return 0;
}
  • func 是我们调用的函数。
  • test 是一个变量,同时是 func 的实际参数 ( argument )。
  • x 也是变量,而且是局部变量,同时是 func 的形式参数 ( parameter )。
  • 实际参数是,它可以由字面量值或者变量提供。
  • 形式参数是变量,它只能是变量。
  • main 是调用者 ( caller )。
  • func 是被调用者 ( callee )。

传值调用 Call by value

值传递策略中,一旦开始函数调用过程,形式参数就会以实际参数的值初始化,且二者互不影响

相当于我有一份 doc 文档,复制了一份给你。你怎么修改你那一份,对我的文档都不会有影响。

如果是值传递,第一段代码相当于:

int test = 2;
int x = test;
x++;

C 语言是传值调用的。

传引用调用 Call by reference

引用传递策略中,函数调用将形式参数就是实际参数的别名,二者是同一个变量

相当于我有一份 doc 文档,你修改这份文档,对我来说是可见的。

如果是引用传递,第一段代码相当于:

int test = 2;
test++;

传共享对象调用 Call by sharing

传值、传引用已经不适用于现代编程语言,现在编程语言一般是传入一个特殊的引用类型,相当实际参数复制了一份地址给形式参数,但我们可以根据这个地址去修改对象,使得该函数之外的作为实际参数的变量也会发生改变。

1974 年,Barbara Liskov 意识到自己的 CLU 语言不是二者的任一种,于是命名了传共享对象调用。Python、Java、JavaScript、Scheme、OCaml 等语言都使用了传共享对象调用。(Evaluation strategy – Wikiwand

传共享对象调用中,函数传递的是一个可以共享的对象,这样就可以达到引用调用的效果:一旦被调用者修改了对象,调用者就可以看到变化。

如果坚持要一分为二,那么现在的 Java、Python 都是传值调用,只不过传的是一个可以被函数改变的对象。例如 Python:

def func(alist):
    alist.append(1)
    alist = [0]

def main():
    src = []
    func(src)
    print(src)

if __name__ == '__main__':
    main()

上面代码会打印出 [1],因为列表是可变对象,append 方法改变了 alist。而赋值局部变量的 alist = [0]对函数调用之外的作用域没有影响。

像 Java 也是这样:

class Test {
    public void addBrand(String[] book) {
        book[0] = "0";
        book = new String[]{"1"};
    }

    public static void main(String[] args) {
        Test test = new Test();
        String[] sci_book = new String[1];
        test.addBrand(sci_book);
        out.println(Arrays.toString(sci_book));
    }
}

打印出 [0] 而非 [1]

在这类语言中赋值是给变量绑定一个新对象,而不是改变对象。

等我考完试接着写!

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注