Go语言指针用法详解(附带实例)
Go 语言中的指针学起来很容易,在 Go 语言中,使用指针执行某些任务更简单。
通过指针,Go 语言的开发者可以控制特定集合的数据结构、分配的数量及内存访问模式。指针对于性能的影响是不言而喻的,对于构建运行良好的系统也是非常重要的。在系统编程、操作系统或网络应用等领域,指针更是不可或缺的。
由于指针的应用较为复杂,Java、C#、Python 等编程语言把指针替换为引用。Go 语言在保留指针的同时,对指针做了很多限制,例如不能对指针进行运算等。
指针和引用的区别在于指针是变量,在内存中具有存储位置;引用是一种形式,例如,引用变量是实际变量的别名,操作引用变量就是在操作实际变量,引用变量在内存中没有存储位置。
变量是用于存储数据的,那么指针存储的数据是什么呢?变量存储在一个或多个连续的内存地址中,如果把指针看作箭头,那么这个箭头指向的是某个变量的内存地址。因此,指针存储的数据就是某个变量的内存地址。
例如,声明一个 32 位的 int 类型变量 x,并将其初始化为 10。因为存储一个 32 位的 int 类型变量需要占用 4 个字节的内存,所以可以把变量 x 存储在如下图所示的 4 个字节中;其中,变量 x 的内存地址从 1 开始,到 4 结束。

图 1 把变量x存储在4个字节中
指针也是变量,它的值是变量的内存地址。虽然不同类型的变量占用不同数量字节的内存,但是不同类型的指针占用的是相同数量字节的内存。这是因为不同类型的指针的值均为表示某个变量的内存地址的数字,存储指针相当于存储数字,需要占用 4 个字节的内存。

图 2 把指针存储在4个字节中
如上图所示,变量 pointerX 表示用于存储变量 x 内存地址的指针,存储在 4 个字节中;其中,变量 pointerX 的内存地址从 5 开始,到 8 结束。因为变量 x 的内存地址是从 1 开始的,所以变量 pointerX 的值为 1。
在 Go 语言中,指针的默认值为 nil。也就是说,如果指针没有存储任何变量的内存地址,那么就把这个指针的值默认设置为 nil。
程序中的每个变量在程序运行时都在内存中拥有一个存储位置。为了获取某个变量在内存中的存储位置,Go 语言提供了 & 字符。把 & 字符置于某个变量前,就能获取这个变量的内存地址(即这个变量在内存中的存储位置);Go语言把这种操作称作“取地址”操作。
“取地址”操作的语法格式如下:
下面的实例演示如何对变量执行“取地址”操作。
【实例 1】获取变量的内存地址。首先,声明并初始化 int 类型且值为 10 的变量 number,以及 string 类型且值为 success 的变量 str。然后,分别格式化输出变量 number 的内存地址和变量 str 的内存地址。代码如下:
例如,使用 var 关键字分别声明指向 32 位的 int 类型指针变量 num,以及 64 位的 float 类型指针变量 flt_num。代码如下:
下面将编写一个程序验证上述说法,代码如下:

图 3 程序引发panic
由运行结果可知,使用赋值运算符“=”把 10 赋值给指针变量 num 的方式是不可行的。
那么,上述代码为什么会发 panic 呢?在 Go 语言中,指针变量是引用类型的变量。在使用引用类型的变量前,不仅要声明这个变量,还要为这个变量分配内存空间;否则,无法存储赋给这个变量的值。
Go 语言的内置函数 new() 可以给引用类型的变量分配内存空间。new() 函数的语法格式如下:
下例演示如何使用 new() 函数初始化指针变量。
【实例 2】使用 new() 函数初始化指针变量。首先,声明指向 32 位的 int 类型指针变量 num。然后,使用 new() 函数为指针变量 num 分配内存空间。接着,使用赋值运算符“=”把 10 赋值给指针变量 num。最后,打印指针变量 num 指向的内存地址。代码如下:
使用“短变量声明”的语法格式能够精简例 2 的代码,即把:
例如,使用“短变量声明”的语法格式分别声明 int 类型的指针变量 num,以及 bool 类型的指针变量 bln,并使用内置函数 new() 为它们分配内存空间;分别打印指针变量 num 和 bln 的值。代码如下:
既然使用内置函数 new() 初始化指针变量并不常用,那么初始化指针变量的常用方式是什么呢?为了回答这个问题,先通过编写代码,实现如下步骤:
代码如下:
使用“短变量声明”的语法格式可以进一步简化初始化指针变量 ptr 的代码:
使用 if 语句判断空指针有如下两种编码格式:
下面演示如何判断指针是否为空指针。代码如下:
当对某个指针变量执行“取值”操作时,要使用
Go 语言把
下例演示对变量执行“取地址”操作的过程,并演示对指针变量执行“取值”操作的过程。
【实例 3】取地址操作符和取值操作符的用法。使用“短变量声明”的语法格式声明并初始化 string 类型的变量 str;声明并初始化指向变量 str 内存地址的指针变量 ptr;分别格式化输出指针变量 ptr 的类型和变量 str 的内存地址;在对指针变量 ptr 执行“取值”操作,并把操作结果赋值给另一个 string 类型的变量 str_value 后,分别格式化输出变量 str_value 的类型和值。代码如下:
在讲解如何使用指针修改变量值之前,按如下步骤编写程序:
代码如下:
指针指向的是变量的内存地址,而非变量的值。在使用*字符对某个指针执行“取值”操作后,就能够获取这个指针指向的变量的值。这样就可以修改这个指针指向的变量的值。
根据这个原理,即可修改上述程序,具体修改的内容如下:
上述程序经修改后,代码如下:
【实例 4】定义用于交换两个变量的值的 exchangeValue() 函数。这个函数中有两个参数,即 int 类型的指针变量 i 和 j。使用 * 字符对指针变量 i 执行“取值”操作,将其指向的变量的值赋值给 int 类型的变量 k。使用 * 字符对指针变量 j 执行“取值”操作,将其指向的变量的值赋值给指针变量 i 指向的变量。把变量 k 的值赋值给指针变量 j 指向的变量。
在 main() 函数中,声明并初始化,值分别为 7 和 11 的两个 int 类型变量 x 和 y。格式化输出变量 x 和 y 的值。调用 exchangeValue() 函数,并向其传递变量 x 和 y 的内存地址。再次格式化输出变量 x 和 y 的值。
代码如下:
通过指针,Go 语言的开发者可以控制特定集合的数据结构、分配的数量及内存访问模式。指针对于性能的影响是不言而喻的,对于构建运行良好的系统也是非常重要的。在系统编程、操作系统或网络应用等领域,指针更是不可或缺的。
由于指针的应用较为复杂,Java、C#、Python 等编程语言把指针替换为引用。Go 语言在保留指针的同时,对指针做了很多限制,例如不能对指针进行运算等。
指针和引用的区别在于指针是变量,在内存中具有存储位置;引用是一种形式,例如,引用变量是实际变量的别名,操作引用变量就是在操作实际变量,引用变量在内存中没有存储位置。
什么是指针
指针又称为指针变量,即指针本身是变量。变量是用于存储数据的,那么指针存储的数据是什么呢?变量存储在一个或多个连续的内存地址中,如果把指针看作箭头,那么这个箭头指向的是某个变量的内存地址。因此,指针存储的数据就是某个变量的内存地址。
例如,声明一个 32 位的 int 类型变量 x,并将其初始化为 10。因为存储一个 32 位的 int 类型变量需要占用 4 个字节的内存,所以可以把变量 x 存储在如下图所示的 4 个字节中;其中,变量 x 的内存地址从 1 开始,到 4 结束。

图 1 把变量x存储在4个字节中
指针也是变量,它的值是变量的内存地址。虽然不同类型的变量占用不同数量字节的内存,但是不同类型的指针占用的是相同数量字节的内存。这是因为不同类型的指针的值均为表示某个变量的内存地址的数字,存储指针相当于存储数字,需要占用 4 个字节的内存。

图 2 把指针存储在4个字节中
如上图所示,变量 pointerX 表示用于存储变量 x 内存地址的指针,存储在 4 个字节中;其中,变量 pointerX 的内存地址从 5 开始,到 8 结束。因为变量 x 的内存地址是从 1 开始的,所以变量 pointerX 的值为 1。
“取地址”操作
指针变量可以存储任何一个变量的内存地址。变量占用内存的字节数量只取决于计算机系统的位数,与变量的值无关。计算机系统的位数是 32 位,变量占用 4 个字节的内存;计算机系统的位数是 64 位,变量占用 8 个字节的内存。在 Go 语言中,指针的默认值为 nil。也就是说,如果指针没有存储任何变量的内存地址,那么就把这个指针的值默认设置为 nil。
程序中的每个变量在程序运行时都在内存中拥有一个存储位置。为了获取某个变量在内存中的存储位置,Go 语言提供了 & 字符。把 & 字符置于某个变量前,就能获取这个变量的内存地址(即这个变量在内存中的存储位置);Go语言把这种操作称作“取地址”操作。
“取地址”操作的语法格式如下:
ptr := &v参数说明如下:
- v:被执行“取地址”操作的变量。
- ptr:指针变量。
下面的实例演示如何对变量执行“取地址”操作。
【实例 1】获取变量的内存地址。首先,声明并初始化 int 类型且值为 10 的变量 number,以及 string 类型且值为 success 的变量 str。然后,分别格式化输出变量 number 的内存地址和变量 str 的内存地址。代码如下:
package main // 声明 main 包 import "fmt" // 导入 fmt 包,用于打印字符串 func main() { // 声明 main()函数 number := 10 // 声明并初始化 int 类型且值为 10 的变量 number str := "success" // 声明并初始化 string 类型且值为 success 的变量 str fmt.Printf("变量 number 的内存地址是%p\n", &number) // 格式化输出变量 number 的内存地址 fmt.Printf("变量 str 的内存地址是%p", &str) // 格式化输出变量 str 的内存地址 }运行结果如下:
变量number的内存地址是0xc000016088
变量str的内存地址是0xc000042250
Go语言指针的使用方法
在 Go 语言中,指针的使用流程分为如下 3 个步骤:声明指针变量、初始化指针变量和访问指针变量的值。1) 声明指针变量
在使用指针前,要使用 var 关键字声明指针。声明指针的语法格式如下:var ptr_name *ptr_type参数说明如下:
- ptr_name:指针变量的变量名。
- *:表示指针,即让指定变量成为一个指针。
- ptr_type:指针变量的类型。
例如,使用 var 关键字分别声明指向 32 位的 int 类型指针变量 num,以及 64 位的 float 类型指针变量 flt_num。代码如下:
var num *int32 //指向32位的int类型指针变量num var flt_num *float64 //指向64位的float类型指针变量flt_num
2) 初始化指针变量
前文中,使用 var 关键字声明指向 32 位的 int 类型指针变量 num。那么,能否使用赋值运算符“=”把 10 赋值给指针变量 num 呢?下面将编写一个程序验证上述说法,代码如下:
package main import "fmt" func main() { var num *int32 //声明指向32位的int类型指针变量num *num = 10 //把 10 赋值给指针变量num fmt.Println("指针变量 num 指向的内存地址:", num) }运行结果如下图所示:

图 3 程序引发panic
由运行结果可知,使用赋值运算符“=”把 10 赋值给指针变量 num 的方式是不可行的。
那么,上述代码为什么会发 panic 呢?在 Go 语言中,指针变量是引用类型的变量。在使用引用类型的变量前,不仅要声明这个变量,还要为这个变量分配内存空间;否则,无法存储赋给这个变量的值。
Go 语言的内置函数 new() 可以给引用类型的变量分配内存空间。new() 函数的语法格式如下:
func new(Type) *Type
- 参数 Type 为指针变量的类型;
- 返回值 *Type 表示指向某个类型的指针。
下例演示如何使用 new() 函数初始化指针变量。
【实例 2】使用 new() 函数初始化指针变量。首先,声明指向 32 位的 int 类型指针变量 num。然后,使用 new() 函数为指针变量 num 分配内存空间。接着,使用赋值运算符“=”把 10 赋值给指针变量 num。最后,打印指针变量 num 指向的内存地址。代码如下:
package main import "fmt" func main() { var num *int32 //指向32位的int类型指针变量num num = new(int32) //为指针变量num分配内存空间 *num = 10 //把 10 赋值给指针变量num fmt.Println("指针变量 num 指向的内存地址:", num) }运行结果如下:
指针变量num指向的内存地址: 0xc00001608
注意,上述程序每次运行后的结果都是不同的,用以表示指针变量 num 在程序运行时指向的内存地址。使用“短变量声明”的语法格式能够精简例 2 的代码,即把:
var num *int32 num = new(int32)简化为:
num := new(int32) //声明指向32位的int类型的指针变量num,并为指针变量num分配内存空间在实际开发中,并不经常使用内置函数 new() 初始化指针变量,这是因为通过内置函数 new() 得到的是指向某个类型的指针,如果不为这个指针赋值,那么这个指针的值就是这个类型的默认值。
例如,使用“短变量声明”的语法格式分别声明 int 类型的指针变量 num,以及 bool 类型的指针变量 bln,并使用内置函数 new() 为它们分配内存空间;分别打印指针变量 num 和 bln 的值。代码如下:
num := new(int) //声明 int 类型的指针变量 num,并为指针变量 num 分配内存空间 bln := new(bool) //声明 bool 类型的指针变量 bln,并为指针变量 bln 分配内存空间 fmt.Println(*num) //打印指针变量 num 的值 fmt.Println(*bln) //打印指针变量 bln 的值运行结果如下:
0 //int 类型的默认值
false //bool 类型的默认值
既然使用内置函数 new() 初始化指针变量并不常用,那么初始化指针变量的常用方式是什么呢?为了回答这个问题,先通过编写代码,实现如下步骤:
- 声明并初始化值为 10 的 int 类型变量 number;
- 声明指向 int 类型的指针变量 ptr;
- 使用 & 字符获取变量 number 的内存地址,让指针变量 ptr 指向变量 number 的内存地址。
代码如下:
var number int = 10 var ptr *int ptr = &number通过这 3 行代码,即可初始化指针变量 ptr,这就是初始化指针变量的常用方式。
使用“短变量声明”的语法格式可以进一步简化初始化指针变量 ptr 的代码:
number := 10 ptr := &number
Go语言空指针
当指针定义后且尚未分配任意变量时,它的值为 nil。nil 指针又称为空指针。nil 在概念上和其他语言的 null、None、NULL 一样都表示零值或空值。使用 if 语句判断空指针有如下两种编码格式:
if(ptr != nil) //ptr 不是空指针 if(ptr == nil) //ptr 是空指针
下面演示如何判断指针是否为空指针。代码如下:
var ptr *int //ptr 不是空指针 if ptr != nil { fmt.Printf("ptr 的值为 :%x\n", ptr) } /* ptr 是空指针 */ if ptr == nil { fmt.Printf("ptr 是空指针!") }运行结果如下:
ptr 是空指针!
Go语言获取指针指向的变量的值
当对某个变量执行“取地址”操作时,要使用&
字符,进而获取指向这个变量的内存地址的指针变量;也就是说,指针变量的值是这个变量的内存地址。当对某个指针变量执行“取值”操作时,要使用
*
字符,进而获取这个指针变量指向的变量的值。Go 语言把
&
字符称作取地址操作符;把 *
字符称作取值操作符。取地址操作符和取值操作符是一对互补操作符。下例演示对变量执行“取地址”操作的过程,并演示对指针变量执行“取值”操作的过程。
【实例 3】取地址操作符和取值操作符的用法。使用“短变量声明”的语法格式声明并初始化 string 类型的变量 str;声明并初始化指向变量 str 内存地址的指针变量 ptr;分别格式化输出指针变量 ptr 的类型和变量 str 的内存地址;在对指针变量 ptr 执行“取值”操作,并把操作结果赋值给另一个 string 类型的变量 str_value 后,分别格式化输出变量 str_value 的类型和值。代码如下:
package main import "fmt" func main() { str := "知识就是力量" ptr := &str fmt.Printf("指针变量 ptr 的类型: %T\n", ptr) fmt.Printf("变量 str 的内存地址: %p\n", ptr) str_value := *ptr fmt.Printf("变量 str_value 的类型: %T\n", str_value) fmt.Printf("变量 str_value 的值: %s\n", str_value) }运行结果如下:
指针变量ptr的类型: *string
变量str的内存地址: 0xc000104220
变量str_value的类型: string
变量str_value的值: 知识就是力量
Go语言修改指针指向的变量的值
指针不仅可以获取其指向的变量的值,还可以修改其指向的变量的值。在讲解如何使用指针修改变量值之前,按如下步骤编写程序:
- 定义修改变量值的 modifyValue() 函数。此函数有一个参数,即 int 类型的变量 number;
- 向 modifyValue() 函数传参后,把变量 number 的值修改为 11;
- 在 main() 函数中,声明并初始化值为 7 的 int 类型变量 number;
- 调用 modifyValue() 函数,并向其传递变量 number;
- 打印变量 number 的值。
代码如下:
package main import "fmt" func modifyValue(number int) { number = 11 } func main() { number := 7 modifyValue(number) fmt.Println("number =", number) }运行结果如下:
number = 7
通过运行结果不难发现,modifyValue() 函数没有把变量 number 的值修改为 11。那么,为了让 modifyValue() 函数实现既定功能应该如何修改程序呢?下面将使用指针修改 modifyValue() 函数。指针指向的是变量的内存地址,而非变量的值。在使用*字符对某个指针执行“取值”操作后,就能够获取这个指针指向的变量的值。这样就可以修改这个指针指向的变量的值。
根据这个原理,即可修改上述程序,具体修改的内容如下:
- 把 modifyValue() 函数中的参数修改为 int 类型的指针变量 number;
- 使用 * 字符对指针变量 number 执行“取值”操作,把指针变量 number 指向的变量值改为 11;
- 在 main() 函数中,当调用 modifyValue() 函数时,先使用 & 字符对变量 number 执行“取地址”操作,再向 modifyValue() 函数传递获取的变量 number 的内存地址。
上述程序经修改后,代码如下:
package main //声明 main 包 import "fmt" //导入 fmt 包,用于打印字符串 func modifyValue(number *int) { //定义修改变量的值的函数,参数是一个指针变量 *number = 11 //使用*字符对这个指针变量执行取值操作,进而将其指向的变量的值修改为 11 } func main() { //声明 main()函数 number := 7 //声明并初始化值为 7 的 int 类型变量 number //调用用于修改变量的值的函数,并向其传递变量 number 的内存地址 modifyValue(&number) fmt.Println("number =", number) //打印变量 number 的值 }运行结果如下:
number = 11
通过声明、初始化变量和赋值运算符“=”交换两个变量的值。那么,有没有其他方式也能实现同样的功能呢?下面的实例演示如何使用指针交换两个变量的值。【实例 4】定义用于交换两个变量的值的 exchangeValue() 函数。这个函数中有两个参数,即 int 类型的指针变量 i 和 j。使用 * 字符对指针变量 i 执行“取值”操作,将其指向的变量的值赋值给 int 类型的变量 k。使用 * 字符对指针变量 j 执行“取值”操作,将其指向的变量的值赋值给指针变量 i 指向的变量。把变量 k 的值赋值给指针变量 j 指向的变量。
在 main() 函数中,声明并初始化,值分别为 7 和 11 的两个 int 类型变量 x 和 y。格式化输出变量 x 和 y 的值。调用 exchangeValue() 函数,并向其传递变量 x 和 y 的内存地址。再次格式化输出变量 x 和 y 的值。
代码如下:
package main //声明 main 包 import "fmt" //导入 fmt 包,用于打印字符串 func exchangeValue(i, j *int) { //定义用于交换变量的值的函数,参数是两个指针变量 k := *i //先使用*字符对指针变量 i 执行取值操作,再将其指向的变量的值赋值给 int 类型的变量 k *i = *j //先使用*字符对指针变量 j 执行取值操作,再将其指向的变量的值赋值给指针变量 i 指向的变量 *j = k //把变量 k 的值赋值给指针变量 j 指向的变量 } func main() { //声明 main()函数 x, y := 7, 11 //声明并初始化两个 int 类型的变量 x 和 y,值分别为 7 和 11 fmt.Printf("x = %d, y = %d\n", x, y) //格式化输出变量 x 和 y 的值 //调用用于交换变量的值的函数,并将其传递变量 x 和 y 的内存地址 exchangeValue(&x, &y) fmt.Printf("x = %d, y = %d\n", x, y) //格式化输出变量 x 和 y 的值 }运行结果如下:
x = 7, y = 11
x = 11, y = 7