9-指针¶
区别于C/C++中的指针,Go中的指针不能进行偏移和运算,是安全指针,
任何程序数据载入内存后,在内存都有他们的地址,这就是指针。 为了保存一个数据在内存中的地址,我们就需要指针。
Go 中的指针操作非常简单:&
取地址符、*
根据地址取值。
Warning
指针是值类型!!!
定义指针、指针取地址¶
idn
:指针名、T
指针基类型、*T
某类型指针、variable
:变量、&variable
:取变量地址
Go 中的值类型(int、float、bool、string、数组、struct结构体)都有对应类型指针,如 *int、*float、*string、*bool、*[5]int
。
func main() {
i := 1
var p *int = &i
fmt.Printf("i: %d \n", i) // i: 1
fmt.Printf("iaddr: %p \n", &i) // addr: 0xc000012090
fmt.Printf("p: %p \n", p) // p: 0xc000012090
fmt.Printf("paddr: %p \n", &p) // addr: 0xc000006028
fmt.Printf("pint Type: %T \n", p) // pint Type: *int
}
指针取值¶
定义指针之后,就可以对指针进行操作了。
指针的操作有两种:&
取地址、*
根据地址取值
func main() {
// 指针取值
a := 10
b := &a
fmt.Println(b) // b 中保存着 a 的地址
fmt.Println(&a) // 取 a 的地址
fmt.Println(a) // a 的值
fmt.Println(*b) // 取 b
}
// ------------------------------------
// Output:
0xc000012090
0xc000012090
10
10
&
和取值操作符*
是一对互补操作符,&
取出地址,*
根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
- 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
- 指针变量的值是指针地址。
- 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
结构体指针¶
结构体指针访问结构体字段 同 结构体变量访问结构体字段一样,使用 .
点操作符,Go 没有 ->
操作符。
eg:
type User struct { // 定义结构体
name string
age int
}
Boii := User { // 定义结构体变量
name: "Boii",
age: 18
}
p := &Boii // 定义结构体指针
fmt.Println(p.name) // 用 . 点操作符访问成员 Boii
指针做形参¶
func fn1(x int) {
x = 100
}
func fn2(x *int) {
*x = 100
}
func main() {
var a int = 10
fn1(a)
fmt.Println(a) // 10
var b *int = &a
fn2(b)
fmt.Println(a) // 100
}
指针做返回值¶
Go 允许使用“栈逃逸”机制将局部变量的空间分配在堆上。 使用这种机制,需要返回指针
eg:
func add(a, b int) *int {
sum := a + b
return &sum
}
func main() {
psum := add(10, 20)
fmt.Println(psum) // 0xc000012090
fmt.Println(*psum) // 30
}
new 和 make¶
new¶
new 是一个内置函数,它的函数签名如下:
Type
表示类型,new函数只接收一个参数,这个参数只能是类型
*Type
表示类型指针,new 函数返回一个指向该类型内存地址的指针
new函数可以接受所有类型,使用new函数可以得到类型的指针,并且这个指针指向的内存地址中存着该类型的零值。
func main() {
a := new(int)
b := new([5]int)
c := new([]string)
d := new(map[int]string)
fmt.Printf("a type: %T \n", a) // a type: *int
fmt.Printf("b type: %T \n", b) // b type: *[5]int
fmt.Printf("c type: %T \n", c) // c type: *[]string
fmt.Printf("d type: %T \n", d) // d type: *map[int]string
fmt.Println(*a) // 0
fmt.Println(*b) // [0 0 0 0 0]
fmt.Println(*c) // []
fmt.Println(*d) // map[]
}
-
以下为错误示例:
从上面例子可以看到,声明了一个指针,默认值为func main() { var i *int fmt.Println(i) *i = 10 // 这里会引发 panic } // ------------------------------------ // Output: <nil> panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x1 addr=0x0 pc=0x97581b] goroutine 1 [running]: main.main() e:/---CODE/GO/src/Hello/main.go:23 +0x7b exit status 2
nil
。 在没有分配内存的情况下去使用指针,会导致panic
。 -
以下为正确示例:
make¶
make 也是用于分配内存,但是只能用于slice切片、map字典、chan通道
的内存创建,而且它返回的类型 就是这三个类型本身,因为这三种类型就是引用类型,所以没必要返回他们的指针了。
make 的函数签名如下:
在使用 slice、map、chan
的时候,都需要使用 make 进行初始化,然后才可以对它们进行操作
func main() {
c := new([]string)
s := make([]string, 10)
d := new(map[int]string)
e := make(map[int]string)
fmt.Printf("c type: %T \n", c) // c type: *[]string | string切片类型的指针
fmt.Printf("s type: %T \n", s) // s type: []string | string切片
fmt.Printf("d type: %T \n", d) // d type: *map[int]string | map类型的指针
fmt.Printf("e type: %T \n", e) // e type: map[int]string | map
}
new 和 make 的区别¶
- new 返回的是 指针,make返回的是 类型本身
- new 可以给所有类型的 指针开辟空间,make 只能用于
slice、map、chan
的 初始化