Go 编程 | 连载 14 - 指针 Pointer
一、指针
指针的概念以及定义
一个变量交换的例子
交换两个变量的值在排序过程中是一个高频操作,交换变量值最直接的方式就是通过一个临时变量来实现交换,在 Go 中可以这么来实现:
func main() {
a := 10
b := 20
switchVal(a, b)
fmt.Println(a, b)
}
// 定义一个函数:交换两个变量的值
func switchVal(a int, b int) {
temp := a
a = b
b = temp
}
执行上述代码,输出结果如下:
10 20
根据结果来看并没有成功的实现交换两个变量的值,为什么?因为整型是值类型,作为函数参数时会将拷贝非副本作为实际的参数。
那么如何修改呢?
func main() {
a := 10
b := 20
switchVal(&a, &b)
fmt.Println(a, b)
}
// 交换两个变量的值
func switchVal(a *int, b *int) {
temp := *a
*a = *b
*b = temp
}
执行上述代码,输出结果如下:
20 10
根据输出结果可以确定,a 和 b 两个变量的变量值交换成功。
指针
上述代码中的 &
在之前的文章有提到过,表示获取变量的内存地址,那么 switchVal 函数中的 *int
又表示什么?
*int
表示指针类型
,指针是一种数据类型,指针中存储的是内存地址,而这个内存地址指向一块具体的内存,这块具体的内存中保存着个中类型的数据。
指针类型包含两个部分,第一个是 *
符号,表示是指针类型,第二个组成部分是基本数据类型标识符,表示指针所执行的内存地址中存储的数据类型。
指针变量与普通变量不同的是,普通变量存储的直接就是变量值,而指针存储的是变量的内存地址。
func main() {
var yankee int = 200
// 定义指针
var zulu *int = &yankee
fmt.Printf("%T, %v, %v\n", yankee, yankee, &yankee)
fmt.Printf("%T, %v, %v\n", zulu, zulu, *zulu)
}
执行上述代码,输出结果如下:
int, 200, 0xc0000b2008
*int, 0xc0000b2008, 200
在普通变量前添加 &
就可以获得该变量的内存地址,在指针变量前添加 *
就可以获取该指针变量中的内存地址所指向的值。如果要修改指针变量内存地址指向的值只需要 *指针变量=newVal
即可。
当然定义指针变量时也可以省略 var
关键字,直接使用 :=
来定义。
func main() {
xray := 1.0
whiskey := "Go"
victor := []string{"Stark", "Thor", "Steve"}
uniform := [...]int{1, 3, 5, 7, 9}
tango := map[string]string{
"code": "Tango 6",
"number": "6",
}
tangoCode := tango["code"]
fmt.Printf("%T, %v\n", &xray, &xray)
fmt.Printf("%T, %v\n", &whiskey, &whiskey)
fmt.Printf("%T, %v\n", &victor[0], &victor[0])
fmt.Printf("%T, %v\n", &uniform[0], &uniform[0])
fmt.Printf("%T, %v\n", &tangoCode, &tangoCode)
}
执行上述代码,输出结果如下:
*float64, 0xc000132008
*string, 0xc000116210
*string, 0xc000118180
*int, 0xc00012a060
*string, 0xc000116220
Go 的指针 和 C 语言的指针的区别
C 和 C++ 都提供了指针而且功能强大,可以进行指针的转换、偏移以及运算等,其他静态类型语言如 Java 和动态类型语言如 Python 则是将指针屏蔽,不提供指针的概念以及运算等。
Go 中的指针相比 C 和 C++ 中的指针做了很多的限制,有更高的安全性,不涉及到指针的运算、转换和偏移等功能。
make 函数和 new 函数
先来定义一个指针变量,给指针变量的内存地址赋值,并输出其中保存在内存地址中的值
func main() {
var p *int
*p = 10
fmt.Println(*p)
}
执行上述代码,输出结果如下:
这里为什么会报错呢?这里错误信息提示无效的内存地址或者空的指针指向,也就是说没有指定的内存地址来保存 10。
要初始化一个指针变量可以使用 new 函数
func main() {
var p *int = new(int)
fmt.Println(*p)
*p = 10
fmt.Println(*p)
}
执行上述代码,输出结果如下:
0
10
new 函数在这里会现申请一个内存空间,然后将内存空间保存的值设置为 int 的默认值既 0,而且 new 函数返回的是一个内存地址。
func main() {
var p2 *int
fmt.Println(p2) // 输出结果为:<nil>
}
其他类型如 Map、Slice 如果只定义不初始化,也会报错。
func main() {
var info map[string]string
info["name"] = "Stark"
fmt.Println(info)
var namesSlic []string
namesSlic[0] = "Thor"
fmt.Println(namesSlic)
}
也可以使用 make 函数来进行初始化,创建内存保存数据,返回一个具体的实例。
func main() {
var info map[string]string = make(map[string]string)
fmt.Println(info)
info["name"] = "Stark"
fmt.Println(info)
var namesSlic []string = make([]string, 3)
fmt.Println(namesSlic)
namesSlic[0] = "Thor"
fmt.Println(namesSlic)
}
在 Go 编程 | 连载 11 - 复杂数据类型 Slice 中使用 new 函数创建一个 Slice 实例,因为 new 函数返回的是内存地址,要获取内存地址中保存的实例则需要使用 *
来获取。
make 函数和 new 函数的区别或者使用场景如下:
- make 和 new 都是用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存;new只是将内存清零,并没有初始化内存。
- make 返回的还是引用类型(实例)本身;而 new 返回的是指向类型的指针(内存地址)。
- make 只能用来分配及初始化类型为 slice,map,channel;new 可以分配任意类型的数据。
相关文章
- Go 编程 | 连载 04 - 字符与字符串类型
- Go 编程 | 连载 07 - 运算符与格式化输出
- go 中 struct 是否可以比较?
- 《Go语言精进之路:从新手到高手的编程思想、方法和技巧1》7-9章笔记
- 化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的定义和使用EP07
- 《Go语言精进之路:从新手到高手的编程思想、方法和技巧1》4-6章笔记
- Go 编程 | 连载 15 - Go 语言的函数
- Go 编程 | 连载 28 - Go 与 JSON
- 2.Go语言之标准库学习记录(2)
- 2.Go语言编程学习课后实践
- Go 协程池
- Go语言中常见100问题-#14 Ignoring package name collisions
- Go高性能系列教程之读懂pprof报告
- 「Go工具箱」一个简单、易用的多错误管理包:go-multierror
- Go语言实现的可读性更高的并发神库
- 2023-03-27:avio_list_dir.c 是 FFmpeg 库自带的一个示例程序,它提供了列出目录中所有文件和子目录的功能,请用go语言改写。
- 再见 Go 面试官:GMP 模型,为什么要有 P? | 极客时间
- Java微服务 vs Go微服务,究竟谁更强!?
- Go-循环语句-for
- Go-并发编程-无缓冲和有缓冲 channel 的区别(二)
- Go语言Test功能测试函数详解
- Go语言连接Oracle数据库实战(go连接oracle)
- 以Go语言操作MySQL:轻松运行数据库各种查询(go语言mysql)
- SQL Server快速上手GO!(sqlserver go)
- 便捷使用Go编程连接Redis(go连接redis)
- 使用Go语言实现Redis数据库(用go实现redis)
- 踏上oracle编程之路,Go(go oracle编程)
- Go语句让Oracle数据库管理更方便(oracle go语句)