李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
GO
正文
09.Golang指针介绍
Leefs
2022-07-01 PM
802℃
0条
[TOC] ### 前言 ![09.Golang指针介绍01.jpeg](https://lilinchao.com/usr/uploads/2022/07/4122780185.jpeg) ### 一、概述 指针也是一种类型,也可以创建变量,称之为指针变量。指针变量的类型为 `*Type`,该指针指向一个 `Type` 类型的变量。指针变量最大的特点就是**存储的某个实际变量的内存地址**,通过记录某个变量的地址,从而**间接的操作该变量**。 ![09.Golang指针介绍02.jpg](https://lilinchao.com/usr/uploads/2022/07/2988897214.jpg) 比如,想把"100"这个数写入程序中作为一个变量,程序一启动将数字"100"加载到内存当中,假设内存地址是`0x0201`),在程序中将数字"100"赋值给变量`x`,将内存地址赋值给变量`y`。这时候变量`y`就是一个指针变量。通过变量`x`和变量`y`都可以获取到数字"100"。 Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:`&`(取地址)和`*`(根据地址取值)。 Go语言的指针类型变量即拥有指针高效访问的特点,又不会发生指针偏移,从而避免了非法修改关键性数据的问题。同时,垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。 ### 二、创建指针 创建指针有三种方法: + **第一种**:定义普通变量,再通过获取该普通变量的地址创建指针 ```go func main(){ // 定义普通变量 x x := "李林超博客" // 取普通变量 x 的地址创建指针 ptr ptr := &x fmt.Println(ptr) //0xc00003c250 } ``` + **第二种**:先创建指针并分配好内存,再给指针指向的内存地址写入对应的值 ```go func main(){ //创建指针 ptr2 := new(string) //给指针指向的内存地址写入对应的值 *ptr2 = "李林超博客欢迎你" fmt.Println(ptr2) //0xc000088220 fmt.Println(*ptr2) //李林超博客欢迎你 } ``` new()函数会在下面篇幅进行详细介绍。 + 第三种:先声明一个指针变量,再从其他变量获取内存地址给指针变量 ```go func main(){ //定义一个变量 x2 x2 := "Hello GO" //声明指针变量 var p *string //指针初始化 p = &x2 fmt.Println(p) //0xc00003c250 fmt.Println(*p) //Hello GO } ``` ### 三、指针地址和指针类型 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用`&`字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:`*int`、`*int64`、`*string`等。 取变量指针的语法如下: ```go ptr := &v // v的类型为T ``` **说明** - v:代表被取地址的变量,类型为`T` - ptr:用于接收地址的变量,ptr的类型就为`*T`,称做T的指针类型。`*`代表指针。 **示例** ```go func main() { a := 10 b := &a fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078 fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int fmt.Println(&b) // 0xc00000e018 } ``` `b := &a`的图示: ![09.Golang指针介绍03.png](https://lilinchao.com/usr/uploads/2022/07/3906420700.png) **提示**:变量、指针和地址三者的关系是,**每个变量都拥有地址**,**指针的值就是地址**。 ### 四、指针取值 在对普通变量使用`&`操作符取地址后会获得这个变量的指针,然后可以对指针使用`*`操作,也就是指针取值。 **示例** ```go //指针取值 func main(){ a := 10 b := &a //取变量a的地址,将指针保存到b中 fmt.Printf("type of b:%T\n", b) c := *b //指针取值(根据指针去内存取值) fmt.Printf("type of c:%T\n", c) fmt.Printf("value of c:%v\n", c) } ``` **运行结果** ``` type of b:*int type of c:int value of c:10 ``` **总结:** 取地址操作符`&`和取值操作符`*`是一对互补操作符,`&`取出地址,`*`根据地址取出地址指向的值。 变量、指针地址、指针变量、取地址、取值的相互关系和特性如下: - 对变量进行取地址(&)操作,可以获得这个变量的指针变量。 - 指针变量的值是指针地址。 - 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。 **指针传值示例** ```go func modify1(x int) { x = 100 } func modify2(x *int) { *x = 100 } func main() { a := 10 modify1(a) fmt.Println(a) // 10 modify2(&a) fmt.Println(a) // 100 } ``` ### 五、使用指针修改值 通过指针不仅可以取值,也可以修改值。 > 示例:通过指针进行数值交换 ```go package main import "fmt" // 交换函数 func swap(a, b *int) { // 取a指针的值, 赋给临时变量t t := *a // 取b指针的值, 赋给a指针指向的变量 *a = *b // 将a指针的值赋给b指针指向的变量 *b = t } func main() { // 准备两个变量, 赋值1和2 x, y := 1, 2 // 交换变量值 swap(&x, &y) // 输出变量值 fmt.Println(x, y) } ``` **运行结果** ``` 2 1 ``` **总结**(重点) `*`操作符作为右值时,意义是取指针的值,作为左值时,也就是放在赋值操作符的左边时,表示 a 指针指向的变量。 其实归纳起来,`*`操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。 如果在 swap() 函数中交换操作的是指针值,会发生什么情况? **示例代码** ```go package main import "fmt" func swap(a, b *int) { b, a = a, b } func main() { x, y := 1, 2 swap(&x, &y) fmt.Println(x, y) } ``` **运行结果** ``` 1 2 ``` 结果表明,交换是不成功的。上面代码中的 swap() 函数交换的是 a 和 b 的地址,在交换完毕后,a 和 b 的变量值确实被交换。但和 a、b 关联的两个变量并没有实际关联。 这就像写有两座房子的卡片放在桌上一字摊开,交换两座房子的卡片后并不会对两座房子有任何影响。 ### 六、new和make **先看一个示例** ```go func main() { var a *int *a = 100 fmt.Println(*a) var b map[string]int b["llc"] = 100 fmt.Println(b) } ``` 执行上面的代码会引发panic,为什么呢? 在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。 要分配内存,就引出来今天的new和make。 Go语言中new和make是内建的两个函数,主要用来分配内存。 #### new new是一个内置的函数,它的函数签名如下: ```go func new(Type) *Type ``` **说明** - Type表示类型,new函数只接受一个参数,这个参数是一个类型 - `*Type`表示类型指针,new函数返回一个指向该类型内存地址的指针。 new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。 **示例:** ```go func main() { a := new(int) b := new(bool) fmt.Printf("%T\n", a) // *int fmt.Printf("%T\n", b) // *bool fmt.Println(*a) // 0 fmt.Println(*b) // false } ``` 本节开始的示例代码中`var a *int`只是声明了一个指针变量a但是没有初始化,**指针作为引用类型需要初始化后才会拥有内存空间,才可以给它赋值**。 应该按照如下方式使用内置的new函数对a进行初始化之后就可以正常对其赋值了: ```go func main() { var a *int a = new(int) *a = 10 fmt.Println(*a) //10 } ``` #### make make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。 make函数的函数签名如下: ```go func make(t Type, size ...IntegerType) Type ``` make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。 本节开始的示例中`var b map[string]int`只是声明变量b是一个map类型的变量,需要像下面的示例代码一样使用make函数进行初始化操作之后,才能对其进行键值对赋值: ```go func main() { var b map[string]int b = make(map[string]int, 10) b["llc"] = 100 fmt.Println(b) //map[llc:100] } ``` #### new与make的区别 1. 二者都是用来做内存分配的。 2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身。 3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。 *附参考文章链接* *http://www.go-edu.cn/2022/05/08/%E6%8C%87%E9%92%88/* *https://www.liwenzhou.com/posts/Go/07_pointer/* *http://c.biancheng.net/view/21.html*
标签:
Golang基础
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2210.html
上一篇
08.Golang容器之Map
下一篇
10.Golang结构体(一)
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
标签云
国产数据库改造
Shiro
gorm
数据结构
并发线程
Linux
RSA加解密
队列
查找
人工智能
Quartz
FileBeat
ClickHouse
Flink
设计模式
JavaWEB项目搭建
Spark Core
DataX
链表
MyBatis-Plus
Spark Streaming
Tomcat
Livy
Spark RDD
nginx
Netty
Sentinel
Ubuntu
机器学习
散列
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞