Go 指针

一、关于指针

要搞明白Go语言中的指针需要先指定3个概念:指针地址、指针类型、指针取值

  • 指针地址(&a)
  • 指针取值(*&a)
  • 指针类型(&a) —> *int 改变数据传指针

  • 变量的本质是给存储数据的内存地址起了一个好记的别名

  • 比如我们定义了一个变量a := 10,这个时候可以直接通过a这个变量来读取内存中保存的10这个值

  • 在计算机底层a这个变量其实对应了一个内存地址

  • 指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址

  • Go语言中的指针操作非常简单,我们只需要记住两个符合:&(取地址)和*(根据地址取值)

package main

import "fmt"

func main() {
    var a =10
    fmt.Printf("%d\n",&a)    //&a指针地址(824633761976)
    fmt.Printf("%d\n",*&a)    //*&a指针取值(10)
    fmt.Printf("%T\n",&a)    //%T指针类型(*int)
}

二、&取变量地址

1、&符合取地址操作
  • 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置
  • Go语言中使用&字符放在变量前面对变量进行取地址操作
  • Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型
  • 取变量指针的语法如下:
ptr := &v    //比如v的类型为T
  • v:代表被曲地址的变量,类型为T
  • ptr:用于接收地址的变量,ptr的类型就为*T,称作T的指针类型,*代表指针
package main

import "fmt"

func main() {
    var a = 10
    var b= &a
    fmt.Printf("a:%d ptr:%p\n",a,&a)    //a:10 ptr:0xc0000100a8
    fmt.Printf("b:%v type:%T\n",b,b)    // b:0xc0000100a8 type:*int
    fmt.PrintIn("取b的地址:",&b)        // 取 b 的地址: 0xc000006028
}
2、b := &a的图示

三、指针修改数据

1、*指针取值
  • 在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用操作,也就是指针取值
package main

import "fmt"

func main() {
    a := 10
    b := &a    //取变量a的地址,将地址保存到指针b中
    fmt.Printf("type of b:%T\n",b)    //type of b:*int
    c := *b    //指针取值(根据指针的值去内存取值)
    fmt.Printf("type of c:%T\n",c)    //type of c:int
    fmt.Printf("value of c:%v\n",c)    //value of c:10
}
  • 变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
    • 对变量进行取地址(&)操作,可以获得这个变量的指针变量
    • 指针变量的值是指针地址
    • 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值
2、指针传值示列
package main

import "fmt"

func modify1(x int) {
    x = 100
}

func modify1(x *int) {
    x = 100
}

func main() {
    a := 10
    modify1(a)
    fmt.Println(a)    // 10
	modify2(&a)
	fmt.Println(a)    // 100
}

四、new和make

1、执行报错
  • 执行下面的代码会引发panic,为什么呢?
  • 在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储
  • 而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间
  • 要分配内存,就引出来今天的new和make
  • Go语言中new和make是内建的两个函数,主要用来分配内存
package main
import "fmt"
func main() {
	var userinfo map[string]string
	userinfo["username"] = "Snail"
	fmt.Println(userinfo)
}
/*
panic: assignment to entry in nil map
 */
2、make和new比较
  • new和make是两个内置函数,主要用来创建并分配类型的内存
  • make和new的区别
    • make关键字的作用是创建slice、map和channel等内置的数据结构
    • new的作用是为类型申请一片内存空间,并返回指向这片内存的指针
package main

import "fmt"

func main() {
    a := make([]int,3,10)    //切片长度为1,预留空间长度为10
    a = append(a,1)
    fmt.Printf("%v--%T\n",a,a)    //[0 0 0]--[]int  值--切片本身

    var b = new([]int)
    //b = b.append(b,2)          // 返回的是内存指针,所以不能直接 append
	*b = append(*b, 3)        // 必须通过 * 指针取值,才能进行 append 添加
	fmt.Printf("%v--%T",b,b)    // &[]--*[]string  内存的指针---内存指针

}
3、new函数
  • 一:系统默认的数据类型,分配空间
package main

import "fmt"

func main() {
    //1.new实例化int
    age := new(int)
    *age =1

    //2、new实例化切片
    li := new([]int)
    *li = append(*li,1)

    //3、实例化map
    userinfo := new(map[string]string)
    *userinfo = map[string]string{}
    {*userinfo}["username"] = "Snail"
    fmt.PrintIn(userinfo)    //&map[username:Snail]
}
  • 二、自定义类型使用new函数来分配空间
package main

import "fmt"

func main() {
    var s *student
    s = new(student)    //分配空间
    s.name = "Snail"
    fmt.PrintIn(s)    //&{Snail 0}
}

type Student struct {
    name string
    age int
}
4、make函数
  • make也是用于内存分配的,但是和new不同,它只用于chan、map以及slice的内存创建
  • 而且它返回的类型就是这三个类型本身,而不是他们的指针类型
  • 因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
package main

import "fmt"

func main() {
    a := make([]int,3,10)    //切片长度为1,预留空间长度为10
    b := make(map[string]string)
    c := make(chan int,1)
    fmt.PrintIn(a,b,c)    //[0 0 0] map[] 0xc0000180e0
}
  • 当我们为slice分配内存的时候,应当尽量预估到slice可能的最大长度
  • 通过给make传第三个参数的方式来给slice预留好内存空间
  • 这样可以避免二次分配内存带来的开销,大大提高程序的性能
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值