为什么要有指针和引用类型?

简单说,是为了必要的,且很基础的表达能力 (描述能力)。

0. 数据四要素:名、值、址、型

指针、引用的基础,就是在描述一个数据时,除了这个数据的“值”以外,引入了这个数据的“地址”(以下也会简称“址”),即数据在哪个位置上。

进一步理解,数据数据的“地址”,表达的是这个数据的“实体”,数据的“值”,表达的是数据的“表象”。

就好比说,你老婆的漂亮值是 100,我老婆的也是,但这两个100并不是在描述同一个实体。

相同的值,可能在表达不同的类型的数据,所以,在值、址之外,数据还需要一个“类型”(以下也会简称“型”)。

最后,除了“值”、“址”、“型”为了方便我们在代码中指定某个数据,代码中的多数据,还有一个属性“名字”比如变量或常量的名字(以下也会简称“名”)。有名数据通常就是变量、常量,无名的数据就是“字面量”。

编译型的代码,在编译之后,“名”这个属性通常没有一直存在的必要性(哪怕有带反射),因为程序绝大多数情况下,可以通过地址来精确指代一个数据。
类似的,“型”在代码表达中极其重要,但在在编译之后的程序里,必要性也不高。

1. 猥琐语言的进化史

好,同学们,下面我来举一个语言的例子,让大家肉眼观察这门语言是如何进化出“指针、引用”的。首先我们需要从基础的,数据据的名、值、址的理解开始。

我们即时发明的一种程序语言,名为 “猥琐”计算机语言,有时也简称WS。

1.1 从字面数据开始

让我们WS语言开始写代码:

18
18
由于网上竟然还没有支持猥琐语言的高亮脚本,所以以下只好委屈 C、C++、Go 等语言了,我们的代码标记为它们以获得高亮。

代码中出现两个18!问题马上来了:这两个18表达的是同一个意思吗?如果是同一个意思?那为什么要有两个18?

1.2 上帝说:要有类型……

也就是说,现在,两个数据都有“值”,但是,它们在表达什么?它们没有类型吗?让我们查询一下WS语言手册有什么内置类型……

哇!好棒,内置类型竟然有 年龄、长度、胸围……

我们选长度:

18 : 长度  // WS语言语法:  值 : 类型
18 : 长度  

有“值”有“型”,但还不够——

  • 它们分别是什么东西的长度?
  • 假设代码中有很多地方需要使用到这个长度,难道一直写 18 ,万一哪天要改成 17呢?
  • 如果就是写 18,那为什么要定义两个18?只有一个就够了呀?

好吧,是时候让“名”登场了:

JJ长 = 18 : 长度   // WS是一门国际化语言,支持数据使用汉字作名字
JJ长 = 18 : 长度

1.3 不要重名,不要重名!

等等!!!一前一后两个数据的“名”竟然完全同名!WS语言制定者,你们太不专业了!

  • WS语言制定者:名字相同有什么不合理吗?现实生活中,你老婆叫王大锤,我老婆也叫王大锤啊!
  • WS语言用户:那我们怎么区分哪个是哪个啊!!!!
  • WS语言制定者:当然是看数据的地址,也就实体啊!地址不同,实体就不同;在你家床上睡的王大锤是你老婆,在我家床上睡的是我老婆啊?有什么区分困难吗?
  • WS语言使用者:可是上面代码中,没有体现数据的地址属性啊。

哦!也对。计算机语言中的数据,是如何体现它们(存放在内存中的)地址不同呢? 让我们借鉴一下其它语言吧,包含但不限于:C、C++、C#、Java、Go……

借鉴完毕,原来这么简单呀:就是不直接在代码使用地址表达数据,但是在有需要时,可以通过数据“名字”来映射到数据的“地址”。

"有需要时" 是重点。说明见后。

这么说,数据的名字,还真是不要允许同名的好,万一要同名的话,两个数据就必须处于不同的上下文中……比如复合语句作用域,比如名字空间……

算了算了,猥琐语言主打简洁而猥琐,就让我们简(猥)单(琐)地规定所有变量不允许同名吧!

我的JJ长 = 18 : 长度   
你的JJ长 = 18 : 长度

好,现在我们拥有两个数据:我的JJ长,和你的JJ长。核心问题来了:这两个数据除了名字不一样以外,它们“型”相同、“值”相同,所以,它们是同一个数据吗?

1.4 地址不同,才是真的不同

当然不是!我的JJ在我这里,你的JJ在你这里。

同学: 老师,偶尔也会在王大锤(们)那里。
老师:你们在说什么?

也就是说,“我的JJ长”和“你的JJ长”,值相同,型相同,名不同,址不同。

“址”肯定代表一个数据实体。而“名”,如前所述,主要是给程序员使用的,它有时代表“值”,有时代表“址”。

[重点]

  • 有的计算机语言,在某些上下文可以自行决定这个名字表达的是“值”,还是“址”,即左值右值之分,在无法自动区分的情况下,当需要从名到址转换时,采用一个特殊的操作来表达,比如C、Go、C++中用特定的“取址操作符”(通常是 &) ;
  • 而有的语言,几乎都可以纯粹通过上下文来判断,比如 Java。这类语言通常就是把“型”和“名”的作用做了硬性绑定:某些“型”的数据,它的“名”只表达值,某些“型”的数据,它的“名”可以表达“址”。

1.5 当数据需要传递……

猥琐语言少不了有函数。现在我们就需要一个叫“增大术”的函数,长这样:

func 增大术 ( jjl : 长度 ) -> boolean {
    if jjl <= 5 :
       输出 "根基太小了,恕本医师无能为力"
       return false
 
    if jjl >= 30 :
       输出 "请不要过份追求肉体上的尺寸!内心的强大才是真正的强大!"
       return false

    jjl++

    return true
}

可以把“增长术”这个函数,理解为一场“手术”,所以它返回手术是否成功,而不是返回新长度;同时,它动刀的是原有长度,同样不是返回新长度。

由此,我们预期:

你的JJ长 = 18 : 长度
增大术 (你的JJ长)

输出 你的JJ长  // 应该得到 19

但是,问题来了, 代码中第二行的, 传入 增大术函数的 你的JJ长 ,现在这个“名”,它表达的是对应数据的“值(表象)”?还是“址”(实体)呢?

显然得是后者!显然没有哪一位大神医师,能通过计算一个18+1得到19,就让你身上的某个东西从18变成19,你必须掏出实体并且放到手术台上啊!

猥琐语言使用者(医生角色):“来,这位患者,请把“你的JJ长”的实体,传送过来,摆在这里,我要动刀(修改)了”

猥琐语言制定者:“等一下,让我想想……”

没错,是时候想一想WS语言的下一步发展了。如前所述,我们有两个选择:

  1. 通过增加明确的表达方式(比如一个符号),来区分一个数据的名字是“值”或是“址”(指针);
  2. 通过将“型”和“名”的作用硬性绑定,让特定的某些类型的数据,名字即可表达数据的“址”(常被称为“引用类型”),而另一些类型的数据,名字就永远只能表达“值”(常被称为“值类型”)

选择1,是C++的路;选择2,是Java的路。

选择在1和2之间来回混的,是 go语言的路。

无论何种选择,反正,指针或引用,就这么引入了。让我们都给个例子:

(一)、 C++风格

func 增大术 ( jjl : *长度 ) -> boolean { // 注意,现在入参的类型是 “ *长度 ”
    ...
}

你的JJ长 = 18 : 长度
增大术 ( & 你的JJ长) // 注意多了一个取址符 & 

输出 你的JJ长  // 得到 19

(二)、Java风格

先规定 长度 是值类型(不能表达址),再特意搞出一种对应类型,叫 “长度的包装箱”,是引用类型:

func 增大术 ( jjl : 长度的包装箱 ) -> boolean { // 注意,现在入参的类型是 “长度的包装箱”
    ...
}

你的JJ长 = 18 : 长度
放在这个箱子里的你的JJ长 = 装箱操作(你的JJ长) // 注意有个装箱操作

增大术 ( 放在这个箱子里的你的JJ长 ) // 注意实参现在放在箱子里再传过去……

你的JJ长 = 拆箱操作(放在这个箱子里的你的JJ长) // 注意这里拆快递了

输出 你的JJ长  // 得到 19

2 最后,回到问题

指针和引用当然还有别的用处,且有既有紧密关系又有重大区别。但都不是问题“为什么要有指针和引用类型”的重点了,因为本文已经讲了“为什么需要指针或引用”的一个必要条件。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南郁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值