什么是指针
基本数据类型和数组,变量存储的是值
,也就是值类型。
指针类型,变量存储的是另外一个变量的内存地址
,也就是指针类型。
package main
import "fmt"
func main() {
var name string = "smile"
var ptr *string = &name
fmt.Printf("变量name的值是%v\nptr指针的值是%v",name,ptr)
}
程序的输出结果:
变量name的值是smile
ptr保存的值是0xc000088220
ptr本身的内存为0xc0000ce018
结合程序,画出内存示意图如下:
在上图中,变量name
的值是smile
,存储在地址为0xc00004c230
的内存中。变量ptr
存储了变量name
的地址。现在可以说ptr
指向name
指针的声明
一个指针变量指向一个值得内存地址
类似于变量和常量,在使用指针前你需要声明指针。指针的声明格式如下:
var var_name *var_type
var_type
为指针类型,var_name为指针的变量名,*
号用于指定变量是作为一个指针。
var ip *int //指向整形
var if *float //指向浮点型
让我们写一些代码
package main
import "fmt"
func main() {
var name string = "smile"
var pname *string
pname = &name
fmt.Printf("panme的类型是%T\n值是%v",pname,pname)
}
'&'操作符用来获取到一个变量的地址。在上面的程序中,我们讲name
的地址赋予pname
(pname
的类型为*string
)。现在我们说pname
指向了name
。程序的输出为:
panme的类型是*string
值是0xc000088220
指针的空值
指针的空值为nil
package main
import "fmt"
func main() {
name := "smile"
var pname *string
if pname == nil {
pname = &name
fmt.Println(pname)
}
}
在上面的程序中 pname
的初始值为nil
。接着将name
的地址赋值给pname
。程序的输出结果为:
pname的值为 <nil>
pname的值为 0xc00004c230
指针解引用
解引用指针的意思是通过指针访问指向的值。指针pname
的解引用就是*pname
让我们通过一个程序看一下它是怎么工作的。
package main
import "fmt"
func main() {
name := "smile"
pname := &name
fmt.Printf("pname的值为%v\n指向的值为%v",pname,*pname)
}
我们将panme
解引用并打印这个解引用得到的值。和我们预期的一样,程序打印的是name
的值smile
,程序的输出为:
pname的值为0xc00004c230
指向的值为smile
让我们写一个程序,该程序使用指针改变name
的值
package main
import "fmt"
func main() {
name := "smile"
pname := &name
fmt.Printf("pname的值为%v\n指向的值为%v\n",pname,*pname)
*pname = "dylan"
fmt.Printf("pname的值为%v\n指向的值为%v\n",pname,*pname)
}
在上面的程序中,我们将pname
指向的值进行了修改,因此name
的值也被改变了。程序的输出结果为:
name的值为smile
name的值为dylan
传递指针给函数
package main
import "fmt"
func main() {
var name string = "smile"
fmt.Println("函数调用之前的值",name)
change(&name)
fmt.Println("函数调用之前的值",name)
}
func change(value *string) {
*value = "dylan"
}
我们name
的地址符传递给函数change
。在函数change
内部,通过解引用修改了name
的值。程序的输出结果如下:
函数调用之前的值 smile
函数调用之前的值 dylan
不要传递指向数组的指针给函数,而是使用切片
假设我们需要通过函数修改一个数组。一个办法是将数组的指针作为参数传递给函数
package main
import "fmt"
func main() {
var names [3]string = [3]string{"smile","易文杰","dylan"}
fmt.Println("调用函数之前",names)
changeArray(&names)
fmt.Println("调用函数之后",names)
}
func changeArray(array *[3]string) {
(*array)[0] = "quest"
array[1] = "aron"
}
上面的程序中,数组names
的地址传递给函数changeArray
在函数中,我们通过解引用的方式将数组的第一个和第二个元素进行重新赋值。程序的输出:
调用函数之前 [smile 易文杰 dylan]
调用函数之后 [quest aron dylan]
array[
1]是
(*array)[1]`的简写
虽然可以通过传递数组指针给函数的方式来修改原始数组的值,但在Go中不是惯用的方式。我们可以使用切片来完成同样的事情。
采用切片方式重写上面的程序
package main
import "fmt"
func main() {
var names [3]string = [3]string{"smile","易文杰","dylan"}
fmt.Println("调用函数之前",names)
changeArray(names[:])
fmt.Println("调用函数之后",names)
}
func changeArray(array []string) {
array[0] = "quest"
array[1] = "aron"
}
我们将一个切片传递给changeArray
函数,在函数内部。切片的第一个和第二个元素被修改。程序的输出为:
调用函数之前 [smile 易文杰 dylan]
调用函数之后 [quest aron dylan]
所以请不要将数组的指针作为参数传递给函数,而是使用切片。这样代码更加简洁,在Go中更常被使用
Go不支持指针运算
package main
import "fmt"
func main() {
var ptr *int
i := 1
ptr = &i
ptr ++
fmt.Println(ptr)
}
上面的程序将会报错:invalid operation: ptr++ (non-numeric type *int)