美文网首页
Go Pointer

Go Pointer

作者: JunChow520 | 来源:发表于2020-12-29 00:39 被阅读0次
  • Go语言为开发人员提供了控制数据结构指针的能力,但并不能进行指针运算。
  • Go允许开发人员控制特定集合的数据结构、分配数量、内存访问模式,对构建运行良好的系统非常重要。
  • 指针对于性能的影响不言而喻,对系统编程、操作系统、网络应用,指针是不可或缺的一部分。

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

变量与指针

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

使用注意

  • 所有的值类型都具有对应的指针类型,其形式为*数据类型
  • 值类型包括基本数据类型中int系列、float系列、boolstring、数组、结构体struct

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

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

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

指针变量

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

  • 指针变量通常会缩写为ptr
  • 指针变量指向任何值的内存地址,禁止将值直接赋给指针变量。
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
  • 指针变量占用的字节大小与所指向的值的大小无关
  • 指针变量指向的内存地址在32位机器上占用4个字节,在64位机器上占用8个字节。
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
  • 指针定义后若没有分配给任何变量则默认值为nil
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,都是指代零值或空值。

相关文章

  • go调用c全面解析

    一、go的指针 pointer type、 uintptr和unsafe.Pointer pointer type...

  • Go pointer

    Outputs:

  • Go Pointer

    Go语言为开发人员提供了控制数据结构指针的能力,但并不能进行指针运算。 Go允许开发人员控制特定集合的数据结构、分...

  • Go 指针

    Go中的指针 翻译自 https://go101.org/article/pointer.html[https:/...

  • const pointer in go

    const 在 go 中的使用可以理解为是var语法一样,不同的是语义一个是常量,一个是变量。pointer在go...

  • Go struct pointer

    Outputs:

  • Go-Pointer

    go 指针 声明指针类型 x 错误写法此时创建的是一个空指针,而空指针无法获取到内存地址,也就无法根据内存地址找到...

  • [译] Go Frequently Asked Question

    本文同步发布于: [译] Go Frequently Asked Questions(FAQ) - Pointer...

  • golang 类型断言取不到地址的问题

    报错信息如下:test\testVar.go:16:19: cannot call pointer method ...

  • golang unsafe 包

    阅读原文 golang unsafe 包 ArbitraryType 和 Pointer Go 语言是强类型语言,...

网友评论

      本文标题:Go Pointer

      本文链接:https://www.haomeiwen.com/subject/jqmxoktx.html