Go part 3 指针,栈与堆

指针类型

要明白指针,需要知道几个概念:指针地址指针类型指针取值

取指针地址

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置,使用 & 放在变量前面进行“取指针地址”操作

var int a = 10
fmt.Println("address:", &a)

// address: 0xc000098010

 

获取指针类型

package main
  
import (
    "fmt"
)

func main(){
    var a int = 100
    addrA := &a
    fmt.Printf("pointer type: %T\n",addrA)
}

运行结果:
pointer type: *int

 

从指针获取指针指向的值

使用 * 方便指针地址前面进行“指针取值”操作

package main
  
import (
    "fmt"
)

func main(){
    var a int = 100
    addrA := &a
    fmt.Println("address:", addrA)

    valueA := *addrA
    fmt.Println("value:", valueA)
}

结果:
address: 0xc000098010
value: 100

 

通过指针修改指针指向的值

package main
  
import (
    "fmt"
)

func main(){
    var a int = 100
    addrA := &a
    fmt.Println("address:", addrA)

    valueA := *addrA
    fmt.Println("value:", valueA)

    // modify
    *addrA = 1
    fmt.Println("modify value:", a)
}

运行结果:
address: 0xc000098010
value: 100
modify value: 1

 

练习1:写一个函数,使用指针交换两个整数的值

package main

import (
    "fmt"
)

func swap(a *int, b *int){
    *a, *b = *b, *a
}


func main(){
    var (
        a int = 100
        b int = 200
    )

    swap(&a, &b)
    fmt.Println(a, b)
}

运行结果:
200 100

 

练习2:使用指针变量获取命令行参数,经过 flag 包解析后,以指针类型返回,可定义变量接收命令行的数据

package main
  
import (
    "flag"
    "fmt"
)

func main(){
    // 接收命令行参数,key,默认值,帮助
    var mode = flag.String("mode", "", "process mode")

    flag.Parse()
    fmt.Println(*mode)
}

调用:
go run pointer_03.go --mode "hello, world"

运行结果:
hello, world

 

创建指针的另一种方法

new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向的值为默认值

package main
  
import (
    "fmt"
)

func main(){
    var str *string = new(string)
    fmt.Println(*str)  
    *str = "hello, world~"
    fmt.Println(*str)
}

运行结果:
// 空字符串
hello, world~

 

栈与堆

什么是栈

栈(Stack)是一种拥有特殊规则的线性表数据结构

只允许往线性表的一端放入数据,之后在这一端取出数据,按照后进先出(LIFO,Last InFirst Out)的顺序,如下图:

往栈中放入元素的过程叫做入栈,入栈会增加栈的元素数量,最后放入的元素总是位于栈的顶部,最先放入的元素总是位于栈的底部

从栈中取出元素时,只能从栈顶部取出,不允许从栈底获取数据,也不允许对栈成员进行任何查看和修改操作

 

什么是堆

堆内存分配类似于往一个房间里摆放各种家具,家具的尺寸有大有小,分配内存时,需要找一块足够大小的空间来摆放家具,经过反复摆放和腾空家具后,房间里会变得乱七八糟,此时再往空间里摆放家具会存在虽然有足够的空间,但各空间分布在不同的区域,无法有一段连续的空间来摆放家具的问题,此时,内存分配器就需要对这些空间进行调整优化,如下图:

 

堆分配内存和栈分配内存相比,堆适合不可预知大小的内存分配。但是为此付出的代价是分配速度较慢,而且会形成内存碎片

堆和栈各有优缺点,该怎么在编程中处理这个问题呢?在 C/C++ 语言中,需要开发者自己学习如何进行内存分配,选用怎样的内存分配方式来适应不同的算法需求。比如,函数局部变量尽量使用栈;全局变量、结构体成员使用堆分配等。程序员不得不花费很多年的时间在不同的项目中学习、记忆这些概念并加以实践和使用。

Go 语言将这个过程整合到编译器中,命名为“变量逃逸分析”。这个技术由编译器分析代码的特征和代码生命期,决定应该如何堆还是栈进行内存分配,即使程序员使用 Go 语言完成了整个工程后也不会感受到这个过程

 

栈内存中通常分配值类型,包括 int,  float,  bool,  string 以及数组和 struct(结构体)

堆内存中通常分配引用类型,通过 GC 回收,包括 pointer,  select,  map,  chan 等

 

posted @ 2019-05-23 11:53  kaichenkai  阅读(398)  评论(0编辑  收藏  举报