之前关于Go的复合类型聊到数组和切片,今天继续看看指针(pointer)和结构体(struct)。
指针(pointer)
取地址符–&
Go具有指针。指针保存了变量的内存地址。
我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。
这里先要说下Go语言的取地址符– &,放到一个变量前使用就会返回相应变量的内存地址。
package main
import "fmt"
func main() {
var a int = 20
var s string = "a"
fmt.Printf("a 变量的存储地址: %x\n", &a)
fmt.Printf("s 变量的存储地址: %x\n", &s)
}
%x是十六进制输出。
可以看出,两次运行的结果中,int型的变量地址不同的,string型的变量地址是相同的,这点是不是和java一样呢?–int是值类型,string是引用类型。
指针的定义
类型 *T 是指向 T 类型值的指针。其零值为 nil 。
var p *int
以上定义了一个int类型的指针。
& 操作符会生成一个指向其操作数的指针。
* 操作符表示指针指向的底层值。
package main
import "fmt"
func main() {
var p *int //定义一个指针变量
fmt.Printf("p 变量的初始存储地址: %x\n", p)
i := 10
p = &i //将指针指向变量i
fmt.Printf("p 变量的存储地址: %x\n", p)
/* 使用指针访问值 */
fmt.Printf("*p 变量的值: %d\n", *p)
fmt.Printf("i 变量的值: %d\n", i)
*p = 20 //通过指针 p 设置 i
fmt.Printf("p 变量的存储地址: %x\n", p)
/* 使用指针访问值 */
fmt.Printf("*p 变量的值: %d\n", *p)
fmt.Printf("i 变量的值: %d\n", i)
}
可以看出,
- 指正定义是默认的地址是0,这时它的值就是nil。
- 当指针指向变量i时,它的地址就变成了变量i的地址,通过&操作符完成,这时他的值就是变量i的值。
- 当通过操作符*改变指针的值时,对应的i的值也发生了变化,以为他们引用的是同一个地址上的值。
这也就是通常所说的“间接引用”或“重定向”。
总结一下,使用指针需要三步,首先,定义指针变量。然后,为指针变量赋值(&)。最后就可以访问指针变量中指向地址的值(*)。
指针的指针
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址。
声明格式如下:
var ptr **int;
以上指向指针的指针变量为整型。
package main
import "fmt"
func main() {
var a int
var ptr *int
var pptr **int
a = 10
/* 指针 ptr 地址 */
ptr = &a
/* 指向指针 ptr 地址 */
pptr = &ptr
/* 获取 pptr 的值 */
fmt.Printf("变量 a = %d\n", a)
fmt.Printf("变量 a 内存地址 %x\n", &a)
fmt.Printf("指针变量 *ptr = %d\n", *ptr)
fmt.Printf("指针变量 *ptr 内存地址 %x\n", &*ptr)
fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr)
fmt.Printf("指向指针的指针变量 **pptr 内存地址 %x\n", &**pptr)
}
可以看出,变量a,ptr,pptr都指向了同一个地址。实际上他们引用的是同一个内存地址,所以他们的值也都是一样的。
总之,指针在Go中的用途还是很广泛的,大部分同C语言的也有些共通指出,但与 C 不同,Go 没有指针运算。
结构体(struct)
结构体( struct )就是由一系列具有相同类型或不同类型的数据构成的字段集合。在结构体中可以为不同项定义不同的数据类型。
定义和初始化
struct关键之用于定义结构体,type 声明就是定义的类型。
type T struct {
name string // name of the object
value int // its value
}
如下的代码:
package main
import "fmt"
type Vertex struct {
X int
Y string
}
func main() {
var v = Vertex{1, "s"}
fmt.Println(v)
fmt.Println(v.X)
fmt.Println(v.Y)
v.X = 10
fmt.Println(v.X)
}
上面的代码定义了一个名为Vertex的结构体,结构体是由int和string两个数据类型构成。
然后在main函数中定义了一个类型为Vertex的变量。通过上面的程序也可以得出,操作一个结构体的内容直接通过.操作符就可以了,不管是取值还是赋值。
结构体指针
结构体指针就是指向结构体的指针,类似于其他指针变量。定义结构体指针,结构体的字段就可以通过结构体指针来访问。
特殊的前缀 & 返回一个指向结构体的指针。
如果我们有一个指向结构体的指针 p ,那么可以通过 (*p).X 来访问其字段 X 。 不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以。
package main
import "fmt"
type Vertex struct {
X int
Y string
}
func main() {
v := Vertex{1, "2"}
p := &v
p.X = 333333333
fmt.Println(v)
fmt.Println(p.X)
fmt.Println((*p).X)
}
可以看出,定义的指向结构体v的指针p,对于p的修改就是修改了之前的结构体v。
有疑问加站长微信联系(非本文作者)