c01_07.rst 4.9 KB
Newer Older
写代码的明哥's avatar
写代码的明哥 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
1.7 数据类型:指针
==================

0. 什么是指针
-------------

当我们定义一个变量 name

.. code:: go

   var name string = "Go编程时光"

此时,name
是变量名,它只是编程语言中方便程序员编写和理解代码的一个标签。

当我们访问这个标签时,机算机会返回给我们它指向的内存地址里存储的值:\ ``Go编程时光``\ 

出于某些需要,我们会将这个内存地址赋值给另一个变量名,通常叫做
ptrpointer的简写),而这个变量,我们称之为指针变量。

换句话说,指针变量(一个标签)的值是指针,也就是内存地址。

根据变量指向的值,是否是内存地址,我把变量分为两种:

-  普通变量:存数据值本身
-  指针变量:存值的内存地址

1. 指针的创建
-------------

指针创建有三种方法

**第一种方法**

先定义对应的变量,再通过变量取得内存地址,创建指针

.. code:: go

   // 定义普通变量
   aint := 1
   // 定义指针变量
   ptr := &aint     

**第二种方法**

先创建指针,分配好内存后,再给指针指向的内存地址写入对应的值。

.. code:: go

   // 创建指针
   astr := new(string)
   // 给指针赋值
   *astr = "Go编程时光"

**第三种方法**

先声明一个指针变量,再从其他变量取得内存地址赋值给它

.. code:: go

   aint := 1
   var bint *int  // 声明一个指针
   bint = &aint   // 初始化

上面的三段代码中,指针的操作都离不开这两个符号:

-  ``&`` :从一个普通变量中取得内存地址
-  ``*``\ :当\ ``*``\ 在赋值操作值的右边,是从一个指针变量中取得变量值,当\ ``*``\ 在赋值操作值的左边,是指该指针指向的变量

通过下面这段代码,你可以熟悉这两个符号的用法

.. code:: go

   package main

   import "fmt"

   func main() {
       aint := 1     // 定义普通变量
       ptr := &aint  // 定义指针变量
       fmt.Println("普通变量存储的是:", aint)
       fmt.Println("普通变量存储的是:", *ptr)
       fmt.Println("指针变量存储的是:", &aint)
       fmt.Println("指针变量存储的是:", ptr)
   }

输出如下

::

   普通变量存储的是: 1
   普通变量存储的是: 1
   指针变量存储的是: 0xc0000100a0
   指针变量存储的是: 0xc0000100a0

要想打印指针指向的内存地址,方法有两种

.. code:: go

   // 第一种
   fmt.Printf("%p", ptr)

   // 第二种
   fmt.Println(ptr)

2. 指针的类型
-------------

我们知道字符串的类型是 string,整型是int,那么指针如何表示呢?

写段代码试验一下就知道了

.. code:: go

   package main

   import "fmt"

   func main() {
       astr := "hello"
       aint := 1
       abool := false
       arune := 'a'
       afloat := 1.2

       fmt.Printf("astr 指针类型是:%T\n", &astr)
       fmt.Printf("aint 指针类型是:%T\n", &aint)
       fmt.Printf("abool 指针类型是:%T\n", &abool)
       fmt.Printf("arune 指针类型是:%T\n", &arune)
       fmt.Printf("afloat 指针类型是:%T\n", &afloat)
   }

输出如下,可以发现用
``*``\ +所指向变量值的数据类型,就是对应的指针类型。

::

   astr 指针类型是:*string
   aint 指针类型是:*int
   abool 指针类型是:*bool
   arune 指针类型是:*int32
   afloat 指针类型是:*float64

所以若我们定义一个只接收指针类型的参数的函数,可以这么写

::

   func mytest(ptr *int)  {
       fmt.Println(*ptr)
   }

3. 指针的零值
-------------

当指针声明后,没有进行初始化,其零值是 nil

.. code:: go

   func main() {  
       a := 25
       var b *int  // 声明一个指针
       
       if b == nil {
           fmt.Println(b)
           b = &a  // 初始化:将a的内存地址给b
           fmt.Println(b)
       }
   }

输出如下

::

   <nil>
   0xc0000100a0

4. 指针与切片
-------------

切片与指针一样,都是引用类型。

如果我们想通过一个函数改变一个数组的值,有两种方法

1. 将这个数组的切片做为参数传给函数
2. 将这个数组的指针做为参数传给函数

尽管二者都可以实现我们的目的,但是按照 Go
语言的使用习惯,建议使用第一种方法,因为第一种方法,写出来的代码会更加简洁,易读。具体你可以参数下面两种方法的代码实现

**使用切片**

.. code:: go

   func modify(sls []int) {  
       sls[0] = 90
   }
       
   func main() {  
       a := [3]int{89, 90, 91}
       modify(a[:])
       fmt.Println(a)
   }

**使用指针**

.. code:: go

   func modify(arr *[3]int) {  
       (*arr)[0] = 90
   }
       
   func main() {  
       a := [3]int{89, 90, 91}
       modify(&a)
       fmt.Println(a)
   }

--------------

|image0|

222
.. |image0| image:: http://image.python-online.cn/image-20200320125724880.png