Golang 语言中的指针介绍

简介: Golang 语言中的指针介绍

介绍

指针是一种数据类型,用来存储值的内存地址,为了便于理解,我们也可以把指针理解为内存地址,指针类型只占用内存 4 个或 8 个字节,在 Golang 语言中,类型名称前加 * 表示该类型的指针类型。

指针类型变量也需要一块内存空间存储值,指针变量的值就是它所指向数据的内存地址,而普通变量的值就是具体存放的数据。不同的指针类型变量之间无法互相赋值,在 Golang 语言中,指针不支持运算,也不能获取常量的指针。

指针定义

在 Golang 语言中,指针定义有 3 种方式:

第一种方式是使用取地址符 & 获取变量的指针(内存地址);

第二种方式是使用 var 关键字声明指针变量,使用 var 关键字声明的变量不能直接赋值和取值,因为它还没有内存地址,它的值是 nil;

第三种方式是使用内置的 new 函数来声明指针类型的变量,new 函数接收一个参数,可以传递类型给它,返回值是传递类型的指针类型。

示例代码:

func main () {
 // 方式 1
 // 定义普通变量 a
 a := 1
 // 定义指针变量 p
 p := &a
 fmt.Println("变量 a 的值为:", a) // 1
 fmt.Println("变量 a 的内存地址为:", p) // 0xc0000ae008
 fmt.Printf("变量 a 的类型为:%T\n", a) // int
 fmt.Printf("变量 p 的类型为:%T\n", p) // *int
 // 方式 2
 var str string
 var p1 *int
 // 不同指针类型变量之间无法互相赋值
  p1 = &str // ./main.go:29:5: cannot use &str (type *string) as type *int in assignment
 // 方式 3
 p2 := new(int)
 fmt.Printf("%v %T\n", p2, p2)
}

03

指针操作

在 Golang 语言中,指针操作包括取值和修改。取值就是获取指针指向的值,只需在指针变量前加 *;修改就是修改指针指向的值,需要注意的是使用 var 关键字声明的指针变量不能直接赋值和取值,因为它还没有分配内存,它的值为 nil,可以使用内置函数 new 给它分配内存。

示例代码:

func main () {
  // 获取指针指向的值
 b := 2
 p3 := &b
 val := *p3
 fmt.Println("变量 val 的值为:", val)
 // 修改指针指向的值
 // 给 *p3 赋值,*p3 指向的值也被修改,因为 p3 指向的内存就是变量 b 的内存地址。
 *p3 = 3
 fmt.Println("*p3 指针指向的值为:", *p3)
 fmt.Println("变量 b 的值为:", b)
 var p4 *int = new(int)
 *p4 = 4
 fmt.Println(*p4)
}

04

指针应用

指针参数:

在 Golang 语言中,函数传递参数只有值传递,传递的实参都是参数原始值的拷贝副本,所以我们传递值类型的参数时,修改参数的值,原始数据不会被修改。但是,如果是指针类型的参数,修改参数的值,原始数据也会被修改,原因是指针类型的参数存储的是内存地址,并且和实参的内存地址相同。

示例代码:

func main () {
  // 值类型参数,实参的值未改变
 mySalary := 80000
 fmt.Printf("变量 mySalary 的内存地址为:%p\n", &mySalary)
 modifySalary(mySalary)
 fmt.Println(mySalary)
 // 指针类型参数,实参的值被改变
 modifySalary2(&mySalary)
 fmt.Println(mySalary)
}
func modifySalary (salary int) {
 fmt.Printf("参数变量的内存地址为:%p\n", &salary)
 salary = 100000
}
func modifySalary2 (salary *int) {
 fmt.Printf("参数变量的内存地址为:%p\n", salary)
 *salary = 100000
}

指针接收者:

在 Golang 语言中,定义一个方法,接收者可以是值类型和指针类型,二者都可以调用方法,因为 Golang 编译器会自动转换,所以二者是等价的。

示例代码:

type worker struct {
 name string
 salary uint
}
func (w *worker) raise () {
 w.salary += 1000
}
func (w worker) raise1 () {
 w.salary += 1000
}
func main () {
  // 值类型调用者
 w := worker{
  name: "frank",
  salary: 5000,
 }
 // 指针类型接收者
 w.raise()
 fmt.Printf("w 的姓名是 %s,薪水是每月 %d\n", w.name, w.salary)
 // 值类型调用者
 w1 :=worker{
  name: "frank1",
  salary: 5000,
 }
 // 值类型接收者
 w1.raise1()
 fmt.Printf("w1 的姓名是 %s,薪水是每月 %d\n", w1.name, w1.salary)
 // 指针类型调用者
 w2 := &worker{
  name: "lucy",
  salary: 5000,
 }
 // 指针类型接收者
 w2.raise()
 fmt.Printf("w2 的姓名是 %s,薪水是每月 %d\n", w2.name, w2.salary)
 // 指针类型调用者
 w3 := &worker{
  name: "lucy1",
  salary: 5000,
 }
 // 值类型接收者
 w3.raise1()
 fmt.Printf("w3 的姓名是 %s,薪水是每月 %d\n", w3.name, w3.salary)
}

那么,应该在什么时候使用指针接收者呢?

我总结了以下两点:

  • 如果需要修改接收者,可以使用指针修改指针指向数据的值。
  • 如果接收者是非 map、slice 和 channel 类型,并且数据比较大,可以使用指针来节省内存。

05

总结

本文我们介绍了 Golang 语言中的指针,和指针定义与操作,并且介绍了指针作为指针参数和指针接收者的应用区别。使用指针虽然可以修改数据的值和节省内存,但是也给开发带来了复杂性,所以为了开发简单,在开发中除了必须使用指针类型外,尽量使用值类型,比如数据小的类型 int、bool 和需要并发安全的代码其实没有必要使用指针。

推荐阅读:

参考资料:

https://golang.org/doc/faq#methods_on_values_or_pointers


目录
相关文章
|
2月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
99 4
Golang语言之管道channel快速入门篇
|
2月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
63 4
Golang语言文件操作快速入门篇
|
2月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
93 3
Golang语言之gRPC程序设计示例
|
2月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
81 4
|
2月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
46 4
Golang语言goroutine协程篇
|
2月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
48 3
Golang语言之Prometheus的日志模块使用案例
|
24天前
|
前端开发 中间件 Go
实践Golang语言N层应用架构
【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。
26 0
|
2月前
|
JSON Go 数据格式
Golang语言结构体链式编程与JSON序列化
这篇文章是关于Go语言中结构体链式编程与JSON序列化的教程,详细介绍了JSON格式的基本概念、结构体的序列化与反序列化、结构体标签的使用以及如何实现链式编程。
37 4
|
2月前
|
Go
Golang语言结构体(struct)面向对象编程进阶篇(封装,继承和多态)
这篇文章是关于Go语言中结构体(struct)面向对象编程进阶篇的教程,涵盖了Go语言如何实现封装、继承和多态,以及结构体内存布局的相关概念和案例。
128 4
|
2月前
|
Go
Golang语言基础之接口(interface)及类型断言
这篇文章是关于Go语言中接口(interface)及类型断言的详细教程,涵盖了接口的概念、定义、实现、使用注意事项以及类型断言的多种场景和方法。
34 4