Golang | 第一章 | 3.指针,栈和堆,变量逃逸
一.指针
※fmt.Printf()的动词:
- fmt.Printf("%T",a): 输出指针a的数据类型
- fmt.Printf("%s",a): 输出指针a指向对象的值.
- fmt.Printf("%p",a): 输出指针a存储的内存地址. fmt.Print(a)输出结果一样
指针实例
package main
import (
"fmt"
)
func main() {
v:="5"
str:=&v //对普通变量使用&取得指针
fmt.Printf("str的数据类型: %T",str)
fmt.Println()
val:=*str //使用*取指针的值
fmt.Printf("val的数据类型: %T",val)
fmt.Println()
fmt.Printf("val的值: %s",val)
}
输出为:
str的数据类型: *string
val的数据类型: string
val的值: 5
指针的其他几种创建方式:
//用new创建
①str:=new(string)
//*加变量名
②*str1="fff"
//正统命名方式
③var a *string ="ffff"
二.栈与堆
栈适合分配大小确定的内存对象, 对象们被放在一串连续的内存地址上, 内存分配和回收非常快
堆适合分配大小不定的内存对象, 对象们不连续地被放在一块内存空间内, 内存的分配回收较慢
变量逃逸:编译器决定代码中使用堆还是栈.Go程序员不会感觉到这个过程
1.逃逸分析
//定义一个空函数,一个返回值为整型的函数
//调用空函数,打印变量a和含参函数
package main
import (
"fmt"
)
func dummy(b int)int{
var c int
c=b
return c
}
func void(){
}
func main() {
var a int
void()
fmt.Println(a,dummy(0))
}
打开cmd,输入
go run -gcflags "-m -l" C:\Users\小岛世界的结局\go\src\main\helloworld.go
这个地址是这样得来的: 
得到结果为:
go\src\main\helloworld.go:22:13: main ... argument does not escape //可忽略
go\src\main\helloworld.go:22:13: a escapes to heap //变量a逃逸到堆
go\src\main\helloworld.go:22:21: dummy(0) escapes to heap //dummy(0)逃逸到堆
0 0
2.取地址发生逃逸
//定义一个结构体
//定义一个返回值为指针的函数
package main
import (
"fmt"
)
type Data struct{
}
func dummy() *Data{
var c Data
return &c
}
func main() {
fmt.Println(dummy())
}
用命令行分析变量逃逸:
go\src\main\helloworld.go:12:6: moved to heap: c //将c移到堆
go\src\main\helloworld.go:19:13: main ... argument does not escape
go\src\main\helloworld.go:19:19: dummy() escapes to heap
&{}
返回值为整型, 编译器让c变量使用栈分配. 因为传值调用是将变量值复制后传给dummy()的返回值,使用栈结构很安全.(栈在函数生命周期结束时释放内存)
返回值为地址,编译器将c变量移到堆,传址调用如果坚持使用栈的话,栈在清除内存后会产生未知错误(堆与函数的生命周期无关)
所以,编译器对变量应该分配在堆或栈的原则是:
- 变量是否被取地址
- 变量是否发生逃逸

浙公网安备 33010602011771号