Go Pointer

2020-12-29  本文已影响0人  JunChow520

变量是用来存储数据的,变量的本质是给存储数据的内存地址起了一个好记得别名。指针也是变量,但它是一种特殊的变量,因为指针存储的数据并非普通的值,而是另一个变量的内存地址。

变量与指针

Go语言中将指针分拆为两个概念

  1. 类型指针

C语言中指针可进行运算,Go语言中不存在指针操作,进而避免内存溢出。

  1. 切片

Go语言的指针类型拥有指针高效访问的特定,又不会发生指针偏移,从而避免非法修改关键性数据的问题。同时,垃圾回收也比较容易对不会发生偏移的指针进行索引和回收。

指针

每个变量在运行时都会拥有一个内存地址,内存地址代表变量在内存中的具体位置,指针的值就是变量的内存地址。

基本数据类型中变量保存的是值,因此又称为值类型。指针类型属于引用类型,因为指针类型的变量保存的并非是一个值,而是一个内存地址,内存地址指向的空间保存的才是最终的值。

Golang中使用&取地址符获取变量的内存地址,使用*获取指针类型所指向的值。

声明指针

创建变量时会在内存中分配内存空间去存储变量的值,每个内存块都具有一个地址以表示当前位置,通常内存地址以16进制数表示。

var varname *vartype

vartype表示指针类型,varname表示指针变量,*星号用于指定变量作为一个指针。

var ptr *int
fmt.Printf("ptr: value = %v, type = %T, addr = %v\n", ptr, ptr, &ptr)
ptr: value = <nil>, type = *int, addr = 0xc000006028

使用指针前需要定义指针变量,然后为指针变量赋值,进而才能访问指针变量中指向地址的值。

i := 10
fmt.Printf("i = %v, type = %T, addr = %p\n", i, i, &i) 
i = 10, type = int, addr = 0xc0000aa058
p := &i
fmt.Printf("p = %v, type = %T, addr = %p, value = %v\n", p, p, &p, *p) 
p = 0xc0000aa058, type = *int, addr = 0xc0000d4020, value = 10

每个变量都拥有内存地址,指针的值就是变量的内存地址。对变量使用&取地址符后会获取变量对应的内存地址,对内存地址使用*后可以获取该地址对应的值。&*是一对互补的操作符,&用于取出地址,*则根据地址取出地址指向的值。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    id := 1
    fmt.Printf("id = %d, addr = %p\n", id, &id)//id = 1, addr = 0xc0000100b0

    addr := &id
    println(addr)//0xc0000100b0
    println(reflect.TypeOf(addr))//(0xf6e8e0,0xf2ea80)

    val := *addr
    println(val)//1
}

创建指针(new)

var p *int
fmt.Printf("p = %v, type = %T\n", p, p) //p = <nil>, type = *int

*p = 100
fmt.Println(*p) //panic: runtime error: invalid memory address or nil pointer dereference

Go语言通过new()函数对指针类型创建一个指针,即是用new()函数创建指针变量,new()函数可以创建一个对应类型的指针,创建过程中会分配内存,被创建的指针指向默认值。

i := new(int)
fmt.Printf("i = %v, type = %T, addr = %p\n", i, i, &i)
i = 0xc0000140a0, type = *int, addr = 0xc000006028

变量是一种使用方便的占位符,用于引用计算机内存地址。变量存储的是一个值,值在内存中拥有一个地址,而指针则保存着变量的内存地址,通过内存地址可以获取变量的值。

Go语言提供的取地址符&放到变量前即可获取变量的内存地址,通过*符号可以将指针的值取出。

i := new(int)
*i = 100
fmt.Printf("i = %v, type = %T, addr = %p, value = %v\n", i, i, &i, *i)
i = 0xc000126058, type = *int, addr = 0xc000150018, value = 100

使用注意

make函数也可以用于内存分配,与new函数不同的是,make只用于slicemapchan的内存创建,其返回的类型是这三种类型本身,而非其指针类型。因为这三种类型是引用类型,因此没有必要返回指针。

func make(t Type, size ...IntegerType) Type

newmake之间的区别在于,new用来给基本类型申请内存,返回的是指针类型。而make只能给slicemapchan申请内存,且返回的是类型本身。

指针变量

指针变量就是保存变量内存地址的变量

var i int = 10
var ptr *int = i//cannot use i (type int) as type *int in assignment
cannot use i (type int) as type *int in assignment
var i int = 10
var ptr *float32 = &i// cannot use &i (type *int) as type *float32 in assignment
cannot use &i (type *int) as type *float32 in assignment
var i int = 10                           //基本类型
fmt.Printf("i = %v, addr = %p\n", i, &i) //i = 10, addr = 0xc000126058

var ptr *int = &i //ptr表示指针类型的变量,其类型是指向int类型的指针
fmt.Printf("ptr = %v, addr = %p, value = %v\n", ptr, &ptr, *ptr)
i = 10, addr = 0xc000126058
ptr = 0xc000126058, addr = 0xc000150020, value = 10
var p *int//nil
*p = 100
fmt.Println(*p) 
//panic: runtime error: invalid memory address or nil pointer dereference

取地址符(&)

Go语言中变量前添加&操作符(取地址符)来获取变量的内存地址,指针的值是带有0x十六进制前缀的一组数据。

var v int = 100
fmt.Printf("v address is %p", &v)//v address is 0xc0000100b0

当使用&取地址操作符对普通变量进行取地址操作时,会得到变量的指针。

var v int = 100
ptr := &v
fmt.Printf("pointer type is %T", ptr)//pointer type is *int

v表示被取地址的变量,变量v的地址使用变量ptr接收,变量ptr的类型为*T,称为T的指针类型,*表示指针。

指针取值(*)

当使用&取地址操作符获取变量的指针后,可对指针使用*指针取值操作符获取对应的值。

var v int = 100

ptr := &v
fmt.Printf("pointer type is %T\n", ptr)//pointer type is *int
fmt.Printf("pointer address is %p\n", ptr)//pointer address is 0xc0000100b0

val := *ptr
fmt.Printf("pointer value type is %T\n", val)//pointer value type is int
fmt.Printf("pointer value is %d\n", val)//pointer value is 100

&取地址操作符和*取值操作符是一对互补操作符,&取出地址,*根据地址获取地址指向的值。

var i int = 10
var ptr *int = &i
*ptr = 100

var j int = 20
ptr = &j
*ptr = 200

fmt.Printf("i = %d, j = %d, *ptr = %d\n", i, j, *ptr)//i = 100, j = 200, *ptr = 200

例如:使用指针实现变量交换

package main

import "fmt"

func swap(x, y *int){
    tmp := *x
    *x = *y
    *y = tmp
}

func main(){
    x, y := 1, 2
    swap(&x, &y)
    fmt.Printf("x = %d, y = %d\n", x, y)//x = 2, y = 1
}

例如:使用指针变量获取命令行的输入参数

Go语言内置的flag包实现了对命令行参数的解析,使用flag.String注册名为mode的命令行参数,flag底层会解析命令行并将值赋给mode *string指针。解析完毕后直接通过注册的mode指针获取到最终的值。

package main

import (
    "flag"
    "fmt"
)
//定义命令行参数:go run --mode = fast
var mode = flag.String("mode", "", "process mode")
func main(){
    //解析命令行参数
    flag.Parse()
    //输出命令行参数
    fmt.Printf(*mode)//fast
}

空指针

当指针被定义后没有分配到任何变量此时其值为nilnil指针又称为空指针。nil在概念上等同于其他编程语言中的nullNonenilNULL,都是指代零值或空值。

上一篇下一篇

猜你喜欢

热点阅读