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变量移到堆,传址调用如果坚持使用栈的话,栈在清除内存后会产生未知错误(堆与函数的生命周期无关)

所以,编译器对变量应该分配在堆或栈的原则是:

  • 变量是否被取地址
  • 变量是否发生逃逸

  

posted @ 2019-10-11 22:50  心碎人俱乐部  阅读(13)  评论(0)    收藏  举报