天天看点

018-变量,值和指针

本来这一篇的标题就是指针。但是我发现有些同学对有些概念比较模糊(虽然不影响你编程),所以还是决定来讲讲。

我默认你学过 c 语言的指针的概念。因此我不会讲解过多的指针的概念。另外,在 go 里关于指针取址(​

​&​

​​)和解引用(​

​*​

​) 操作,和 c 语言是一样的,不再重复说明。

本节的目的是澄清一些概念。如果你学的不错,完全可以跳过~~~(希望你不会错过什么。)

1. 变量和 object

gopl 中对变量是这样解释的:

A variable is a piece of storage containing a value.

变量,可以『指示』一块保存了对应类型值的内存。变量可以有自己的『名字』(言外之意也就是它可以没有名字)。

指示一词在这里的含义是:代表、表示,化身。指示一块内存,意思是说,变量这个玩意儿它将这『一块内存』视为『一物』。

在这里,我们将『保存了值的一块内存』称为 object,而变量可以用来『追踪』、『指示』此 object。

上面这段话有点绕,从侧面来解释一下,object 是唯一的,但是可以有多个变量来指示它,或者说用来表示它。

约定:以后我们说变量 x,是说 x 这个变量名指示(表示)的那一块内存。

018-变量,值和指针

图1 object

变量具有一些特性:

  • 可以从变量里提取『值』。
  • 可以根据变量找到它指示的那段内存地址。
  • 可以通过赋值运算符(​

    ​=​

    ​)修改其指示的 object 的值。

如果具备以上几点特征,我们可以称之其为变量,有一些语言里也会把它称为『左值』,有人把『左值』称为可寻址的值(addressable value)。

而『右值』,仅仅只是值而已,不具备以上三点任何特征。它不能寻址,不能对其应用取址操作,只能出现在赋值符号右侧。

最后再总结一下,只要可以用来指示『一块内存』,都可以称其为变量(左值)。

2. 指针(pointer)

在图 1 中 object 的内存首地址,可以称为指针。这个地址的长度,取决于你的处理器架构。比如 32 位架构,首地址长度就是 4 字节;64 位架构,首地址长度就是 8 字节。

2.1 指针变量

指针也可以保存在内存里,那么指示这种『内存』的变量,就可以称为指针变量。当然,指针不一定非得要保存在内存里,只是说如果指针保存在变量里,用起来会方便。

约定:以后为了方便描述,如果我说有一个指针保存在了变量 p 里,而不再说指针保存在一块内存里,有一个名为 p 的变量指示了这一块内存。

2.2 再回忆变量是什么

如果有一个指针 ​

​p​

​​,类型是 ​

​*int​

​​,​

​p​

​​ 保存的是一块内存的地址,那么表达式 ​

​*p​

​​ 将指示『一块内存』。言外之意就是说,​

​*p​

​ 是变量。它具备第 1 节中描述的三个特征:

  • 可以从变量里提取『值』。如​

    ​var x = *p​

  • 可以根据变量找到它指示的那段内存地址。如​

    ​&(*p)​

  • 可以通过赋值运算符(​

    ​=​

    ​​)修改其指示的 object 的值。如​

    ​*p = 10​

因此,可以说 ​

​*p​

​ 是一个变量,也可以说它是左值。

比如下面一段代码:

package main

import "fmt"

func main() {
    var x int = 10
    var p *int = &x

    fmt.Println(&x)
    fmt.Println(*p)    // 取值
    fmt.Println(&(*p)) // 取地址
    *p = 20            // 赋值      

上面这段代码,有一块内存,它是一个 object,保存了数据​

​10​

​​,有两个变量指示了这块内存,一个是变量 ​

​x​

​​,另一个是变量 ​

​*p​

​.

这个程序印证了第 1 节中的一段话:

object 是唯一的,但是可以有多个变量来指示它。

2.3 指针变量的零值

在 go 里,指针变量的零值是 ​

​nil​

​.

我们可以使用 ​

​if p != nil​

​ 来对指针进行测试。

2.4 返回局部变量的指针

在 go 里,允许返回局部变量的指针!

这得益于 go 的垃圾自动回收机制。我们可以在函数外面使用其局部变量的指针,不过这会导致该局部变量被延时释放。

3. new 函数

func new(Type) *Type      

在 go 里,使用 new 函数来分配内存,返回 ​

​*Type​

​ 类型的指针。很像 C++ 里的 new 操作符,但在 go 里,它是函数。

这个函数的参数是 type 而不是 value. 例如:

p := new(int32)      

它表示申请一块内存用于存放 ​

​int32​

​​ 类型的数据,并返回该内存的地址。前面我们还学过一个类似的函数,名为 ​

​make​

​,它的第一个参数也是 type 而不是 value.

喜欢学习的同学可能会问这样的函数到底是怎么定义的?很抱歉暂时我们还定义不了这样的函数,像 ​

​new​

​​, ​

​make​

​ 这样的函数,是由编译器在编译时将其链接到真正的函数上去的。

下面两个函数 ​

​newInt​

​,返回 int 类型变量的地址,在 go 里并没有什么区别:

func newInt() *int{
    var dummy int
    return      
func newInt() *int {
    return new(int)
}      

4. 总结

  • 变量
  • object
  • 指针
  • 指针变量的零值
  • 返回局部变量的指针
  • new 函数