Go 语言指针 | 青训营

88 阅读10分钟

Go 语言指针

Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务。

接下来让我们来一步步学习 Go 语言指针。

我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

以下实例演示了变量在内存中地址:

实例

package main

import "fmt"

func main() {
var a int = 10  

   fmt.Printf("变量的地址: %x\n", &a  )
}

执行以上代码输出结果为:

变量的地址: 20818a220

现在我们已经了解了什么是内存地址和如何去访问它。接下来我们将具体介绍指针。


什么是指针

一个指针变量指向了一个值的内存地址。

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type

var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */

本例中这是一个指向 int 和 float32 的指针。


如何使用指针

指针使用流程:

  • 定义指针变量。
  • 为指针变量赋值。
  • 访问指针变量中指向地址的值。

在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。

实例

package main

import "fmt"

func main() {
var a int= 20   /* 声明实际变量 */
var ip int        / 声明指针变量 */

   ip = &a  /* 指针变量的存储地址 */

   fmt.Printf("a 变量的地址是: %x\n", &a  )

   /* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )

   /* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}

以上实例执行输出结果为:

a 变量的地址是: 20818a220
ip 变量储存的指针地址: 20818a220
*ip 变量的值: 20

Go 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

一个指针变量通常缩写为 ptr。

查看以下实例:

实例

package main

import "fmt"

func main() {
var  ptr *int

   fmt.Printf("ptr 的值为 : %x\n", ptr  )
}

以上实例输出结果为:

ptr 的值为 : 0

空指针判断:

if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

Go指针更多内容

接下来我们将为大家介绍Go语言中更多的指针应用:

内容描述
[Go 指针数组]你可以定义一个指针数组来存储地址
[Go 指向指针的指针]Go 支持指向指针的指针
[Go 向函数传递指针参数]通过引用或地址传参,在函数调用时可以改变其值

Go 语言结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:

  • Title :标题
  • Author : 作者
  • Subject:学科
  • ID:书籍ID

定义结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:

variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

实例如下:

实例

package main

import "fmt"

type Books struct {
title string
author string
subject string
book_id int
}

func main() {

    // 创建一个新的结构体
fmt.Println(Books{"Go 语言", "1", "Go 语言教程", 6495407})

    // 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "1", subject: "Go 语言教程", book_id: 61})

    // 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "1"})
}

输出结果为:

{Go 语言 www.runoob.com Go 语言教程 6495407}
{Go 语言 www.runoob.com Go 语言教程 6495407}
{Go 语言 www.runoob.com  0}

访问结构体成员

如果要访问结构体成员,需要使用点号 . 操作符,格式为:

结构体.成员名"

结构体类型变量使用 struct 关键字定义,实例如下:

实例

package main

import "fmt"

type Books struct {
title string
author string
subject string
book_id int
}

func main() {
var Book1 Books        /* 声明 Book1 为 Books 类型 /
var Book2 Books        /
声明 Book2 为 Books 类型 */

   /* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "1"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407

   /* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "1"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700

   /* 打印 Book1 信息 */
fmt.Printf( "Book 1 title : %s\n", Book1.title)
fmt.Printf( "Book 1 author : %s\n", Book1.author)
fmt.Printf( "Book 1 subject : %s\n", Book1.subject)
fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id)

   /* 打印 Book2 信息 */
fmt.Printf( "Book 2 title : %s\n", Book2.title)
fmt.Printf( "Book 2 author : %s\n", Book2.author)
fmt.Printf( "Book 2 subject : %s\n", Book2.subject)
fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id)
}

以上实例执行运行结果为:

Book 1 title : Go 语言
Book 1 author : 1
Book 1 subject : Go 语言教程
Book 1 book_id : 6495407
Book 2 title : Python 教程
Book 2 author : 1
Book 2 subject : Python 语言教程
Book 2 book_id : 6495700

结构体作为函数参数

你可以像其他数据类型一样将结构体类型作为参数传递给函数。并以以上实例的方式访问结构体变量:

实例

package main

import "fmt"

type Books struct {
title string
author string
subject string
book_id int
}

func main() {
var Book1 Books        /* 声明 Book1 为 Books 类型 /
var Book2 Books        /
声明 Book2 为 Books 类型 */

   /* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "1"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407

   /* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "1"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700

   /* 打印 Book1 信息 */
printBook(Book1)

   /* 打印 Book2 信息 */
printBook(Book2)
}

func printBook( book Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}

以上实例执行运行结果为:

Book title : Go 语言
Book author : 1
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Python 教程
Book author : 1
Book subject : Python 语言教程
Book book_id : 6495700

结构体指针

你可以定义指向结构体的指针类似于其他指针变量,格式如下:

var struct_pointer *Books

以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:

struct_pointer = &Book1

使用结构体指针访问结构体成员,使用 "." 操作符:

struct_pointer.title

接下来让我们使用结构体指针重写以上实例,代码如下:

实例

package main

import "fmt"

type Books struct {
title string
author string
subject string
book_id int
}

func main() {
var Book1 Books        /* 声明 Book1 为 Books 类型 /
var Book2 Books        /
声明 Book2 为 Books 类型 */

   /* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "1"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407

   /* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "1"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700

   /* 打印 Book1 信息 */
printBook(&Book1)

   /* 打印 Book2 信息 */
printBook(&Book2)
}
func printBook( book *Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}

以上实例执行运行结果为:

Go 语言切片(Slice)

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。


定义切片

你可以声明一个未指定大小的数组来定义切片:

var identifier []type

切片不需要说明长度。

或使用 make() 函数来创建切片:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

也可以指定容量,其中 capacity 为可选参数。

make([]T, length, capacity)

这里 len 是数组的长度并且也是切片的初始长度。

切片初始化

s :=[] int {1,2,3 } 

直接初始化切片,[] 表示是切片类型, {1,2,3} 初始化值依次是 1,2,3,其 cap=len=3

s := arr[:] 

初始化切片 s,是数组 arr 的引用。

s := arr[startIndex:endIndex] 

将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。

s := arr[startIndex:] 

默认 endIndex 时将表示一直到arr的最后一个元素。

s := arr[:endIndex] 

默认 startIndex 时将表示从 arr 的第一个元素开始。

s1 := s[startIndex:endIndex] 

通过切片 s 初始化切片 s1。

s :=make([]int,len,cap) 

通过内置函数 make() 初始化切片s[]int 标识为其元素类型为 int 的切片。


len() 和 cap() 函数

切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

以下为具体实例:

实例

package main

import "fmt"

func main() {
var numbers = make([]int,3,5)

   printSlice(numbers)
}

func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

以上实例运行输出结果为:

len=3 cap=5 slice=[0 0 0]

空(nil)切片

一个切片在未初始化之前默认为 nil,长度为 0,实例如下:

实例

package main

import "fmt"

func main() {
var numbers []int

   printSlice(numbers)

   if(numbers == nil){
fmt.Printf("切片是空的")
}
}

func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

以上实例运行输出结果为:

len=0 cap=0 slice=[]
切片是空的

切片截取

可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound] ,实例如下:

实例

package main

import "fmt"

func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}  
printSlice(numbers)

   /* 打印原始切片 */
fmt.Println("numbers ==", numbers)

   /* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])

   /* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])

   /* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])

   numbers1 := make([]int,0,5)
printSlice(numbers1)

   /* 打印子切片从索引  0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)

   /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)

}

func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

执行以上代码输出结果为:

len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]

append() 和 copy() 函数

如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。

下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。

实例

package main

import "fmt"

func main() {
var numbers []int
printSlice(numbers)

   /* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)

   /* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)

   /* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)

   /* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)  
}

func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

以上代码执行输出结果为:

len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]