如何 “区分”值类型与引用类型

一、为什么会出现这两种?

值类型与引用类型之间的区别一直是一个很抽象的话题, 本文就从一个不一样的视角去看待这个问题:

值类型和引用类型对于变量来说的, 它可以看成是变量的一种属性:

var a = 1;   //变量a是一个值类型
var b = [1, 2, 3, 4]; //b是一个引用类型, 得到的是后面数组的地址值

int a = 2;
int[] b = [1, 2, 3, 4];

我们经常说传递值会发生复制, 而传递引用是新的指针的指向原来的指针值, 为什么需要这样做呢? 原因有两个:

  1. 占用内存比较小的数据确定的值,发生复制完全不影响效率。
  2. 占用内存比较大的数据(值组合类型),使用引用来指向, 在传递的时候, 新的指针指向原来的指针值,共享内存, 减少复制导致的性能损失。

二、如何区分?

所有从上面这两点来看, 引入引用类型只是为了效率的提升, 并无其它。所以在一些情况下, 这两者在概念上是完全不做区分的。

  1. 指针类型的解引用操作(dref),这使得你可以使用*x = 2这样的语句来改变引用指向的内容, 导致共享地址的其它引用看到新的内容。但是你不可以使用x=2这样的语句来得到新的值。所以你感觉到值类型的存在。

    var a : i32 = 3;
    	
    var p : *i32 = &a; 
    *p = 2; //改变引用指向的内容
    	
    var b : i32 = a;
    b = 2;
    
  2. 像struct这样的"值组合类型", 你可以通过x.foo=2这样的成员赋值改变引用数据, 比如class object的一部分, 使得共享地址的其它引用看到新的值, 你没法通过成员赋值让另外一个struct变量得到新的值, 所以你感觉到值类型的存在。

    Note: 使用struct这样声明的变量是类型, 然后使用这种类型去声明变量

    struct Point {
        x: i32;
    };
    	
    Point p1 = {x : 2}
    p.x = 2;
    
    struct Point p2 = p1;
    

而在Java、Python中, 即使你将那些原始类型的变量看成是引用, 你确不可以对它进行引用类型所持有,而值类型没有的操作。

你对原始类型只能做这两件事情:

  1. 取出这个变量的值
  2. 为这个变量赋新的值

同时在Java中, 对结构体这样的引用类型你修改它的一部分, 另外一个结构体也会得到新的值。它的行为是一致的。

所以我们可以将这两者统一看成是引用类型, 只不过施加的对象不同罢了

var b = 1;
var b1 = b;

var a = new A();
var a1 = a;

三、值放在堆上和放在栈上?

对于这个问题一种常见的误导人的答案就是:

  • 原始类型存储在栈上, 对象存储在堆上
  • 少量数据存储在栈上, 大量的数据存储在堆上

这样的理解是有错误的, 这种技法就和只背公式一样, 甚至会出现错误。正确的理解方式应该从程序的执行角度去思考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值