【Go语言】指针类型

67 阅读11分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

指针的声明分析

1. 结构:

var 指针变量名 取值符 指针类型

结构分解:img

解析: 指针是存储另一个变量的内存地址的变量。所以指针也是一种变量,只不过它是一种特殊变量,它的值存放的是另一个变量的内存地址。(指针存储的是变量地址)。*当在指针类型变量前面加上 (取值符)可以来获取指针所指向的内容。而在值类型变量前面加上 &(取地址符)可以来获取该变量的指针。 但需要注意的是Go语言的指针类型指向的是变量在底层的内存,但是go语言的指针不允许偏移和运算。

注意点:取地址操作符&和取值操作符*是一对互补操作符。

2. 举例: 请获取一个字符串类型的变量地址并将它赋值给一个指针变量进行输出。

 package main                                                     //包的声明
 import "fmt"                                                      //引入包
 func main() {                                                       //函数
    // 准备一个字符串类型
    var msg = "知链-区块链人才培养摇篮"                             //变量的初始化
    // 对字符串取地址, ptr类型为*string
    ptr := &msg                                                  //指针变量的初始化
    //TODO: 1、打印ptr的类型
    fmt.Printf("ptr type(指针类型): %T\n", ptr)                    //语句&表达式输出
    //TODO: 2、打印ptr的指针地址
    fmt.Printf("address(指针地址): %p\n", ptr)
    //TODO: 3、对指针进行取值操作
    value := *ptr                                                  //指针变量取值
    // 取值后的类型
    fmt.Printf("value (指针值类型)type: %T\n", value)           //语句&表达式输出
    // 指针取值后就是指向变量的值
    fmt.Printf("value(指针值): %s\n", value)
 }

终端输出: img

结构解析:

 package main                                               //包的声明

包声明:package main 定义了包名。它在源文件中指明了这个文件属于那个包。

 import "fmt"                                                      //引入包

引入包: import ("fmt""time") 告诉 Go 编译器这个程序需要使用 fmt 包和time包,fmt 包实现了格式化 IO(输入/输出)的函数。

 func main() {                                                        //函数

函数:func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。函数执行开始后,函数内语句依照main()函数内依次执行。

 // 准备一个字符串类型
    var msg = "知链-区块链人才培养摇篮"                             //变量的初始化

变量的初始化: Go语言是静态类型语言,因此变量(variable)是有明确类型的。编译器会检查函数调用中,变量类型的正确性。在数学概念中,变量表示没有固定值且可改变的数。但从计算机系统实现角度来看,变量是一段或多段用来存储数据的内存。

 // 对字符串取地址, ptr类型为*string
    ptr := &msg                                                  //指针变量的初始化

指针变量的初始化: 指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。这个值代表的是某个内存地址的值,且在这个内存地址往往是内存中存储的变量值的初始位置。在该语句中通过短类型推导式:=声明语句,并通过&取地址符来获取值类型字符串变量值的指针并赋值给变量ptr。

 //TODO: 1、打印ptr的类型
    fmt.Printf("ptr type(指针类型): %T\n", ptr)                    //语句&表达式输出
    //TODO: 2、打印ptr的指针地址
    fmt.Printf("address(指针地址): %p\n", ptr)   

语句&表达式输出: fmt.Printf(...) 可以将字符串输出到控制台,在该输出中Printf 函数支持格式化输出字符串,输出内容前端加上对应的占位符就能输出相应的格式化字符串内容* *并在最后增加换行字符 \n。函数内语句依照main函数内依次执行。**在获取到字符串地址的指针变量以后,对指针变量的类型和地址进行输出。

 //TODO: 3、对指针进行取值操作
    value := *ptr                                                  //指针变量取值

指针变量取值:对指针变量进行取值操作使用操作符,可以获得指针变量指向的原变量的值。在该语句中,通过在指针类型变量前面加上(取值符)来获取指针变量所指向的取值内容。

 // 取值后的类型
   fmt.Printf("value (指针值类型)type: %T\n", value)           //语句&表达式输出
   // 指针取值后就是指向变量的值
   fmt.Printf("value(指针值): %s\n", value)

语句&表达式输出: fmt.Printf(...) 可以将字符串输出到控制台,在该输出中Printf 函数支持格式化输出字符串,输出内容前端加上对应的占位符就能输出相应的格式化字符串内容* *并在最后增加换行字符 \n。函数内语句依照main函数内依次执行。**本语句在获取到字符串地址的指针变量以后,对指针变量的类型和地址进行输出。

指针的使用分析

1. 基本介绍:

指针的基本使用一般用于数据的传递,而在go语言的方法或函数中只有一个传递方式,那就是值传递。每次将变量作为参数传递时,都会创建一个新的变量副本并将其传递给所调用的函数或方法。副本分配在不同的内存地址。

2. 指针传递分析:

指针类型传递,可以达到修改变量值的目的。指针类型的拷贝是浅拷贝,拷贝后的值,指向的内存地址和原值的内存地址一样,修改其中一个值,这个内存地址上的值都会改变,另外大量数据传递时,可以传递指针地址。

3. 举例:定义一个函数返回指针类型的函数并实现赋值传递。

释义: 指针类型的传递不是真实的地址传递,而是指向原有数据的存储的内存地址副本。

func main() {                                                         //函数
   fmt.Println(getzz())
   fmt.Println(*getzz())
}
//定义一个函数返回指针类型
func getzz() *int {                                                  //定义指针函数
   var x *int
   var cnumber int = 2018
   x = &cnumber
   fmt.Println(*x)
   return x
}

终端输出:img

结构解析:

func main() {                                                         //函数
   fmt.Println(getzz())
   fmt.Println(*getzz())
} 

函数:func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。函数执行开始后,函数内语句依照main()函数内依次执行。

//定义一个函数返回指针类型
func getzz() *int {                                                  //定义指针函数
   var x *int                                                     //指针类型变量声明
   var cnumber int = 2018                                      //整数类型的初始化
   x = &cnumber                                              //指针变量赋值
   fmt.Println(*x)                                               //语句&表达式输出
   return x                                                     //返回值输出
}

定义指针函数: 在该程序中定义getzz()函数方法进行封装,将指针类型的传递方法封装保存,在main()主函数需要时直接调用即可,在该语句中我们需要注意的是,我们对指针传递起到的作用要清晰,指针传递的依然是数据的内存地址,但如果改变内存地址的值,则内存地址上原有存的数据也会发生改变。

细化分析:

var x *int                                                     //指针类型变量声明
   var cnumber int = 2018                                      //整数类型的初始化
   x = &cnumber                                              //指针变量赋值

变量的初始化:因为* Go*语言是静态类型语言,因此变量(variable)是有明确类型的。编译器会检查函数调用中,变量类型的正确性。在数学概念中,变量表示没有固定值且可改变的数。但从计算机系统实现角度来看,变量是一段或多段用来存储数据的内存。 变量的声明,赋值过程都可叫做变量的初始化过程,在Go语言中给变量的初始化分为三个阶段,先创建变量,在内存中开辟空间后再初始化变量,将变量初始化为underfined,最后再进行真正赋值从而完成对变量的完整赋值操作。

fmt.Println(*x)                                               //语句&表达式输出

语句&表达式输出: fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。函数内语句依照函数内的编译顺序依次执行。输出语句在调用函数方法后获取到输出结果进行打印。

return x                                                     //返回值输出

返回值输出:当函数执行完的时候,并不是所有时候都要把结果打印,我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西,也就是返回值。函数通过return返回一个返回值。该语句中return关键字后接了一个指针变量x,因此该函数指针编译完成后返回值就是指针变量x的值。

有趣的是:

该语句中return关键字后接了返回值并最后输出返回,但当该关键字return不接返回值时,该函数也是可以完整执行的,只是函数会返回一个undefined, 主函数无法调用到该函数中所处理的数据。

指针的交换函数分析

1. 释义:

指针的交换函数实际上是对变量值赋值交换的一种演变,通过临时变量的值的交换完成了指针类型交换的函数方法。

*当在指针类型变量前面加上 (取值符)可以来获取指针所指向的内容。而在值类型变量前面加上 &(取地址符)可以来获取该变量的指针。

2. 举例:现在我们使用指针交换方法来完成对两个整型变量的赋值交换。

func main() {                                                          //主函数
   // 准备两个变量, 赋值1和2
   x, y := 1, 2
   // 交换变量值
   swapChange(&x, &y)
   // 输出变量值
   fmt.Println(x, y)
}
func swapChange(a, b *int) {                                     // 交换函数封装
   // 取a指针的值, 赋给临时变量t
   t := *a
   // 取b指针的值, 赋给a指针指向的变量
   *a = *b
   // 将a指针的值赋给b指针指向的变量
   *b = t
}

终端输出:img

结构解析:

func main() {                                                          //主函数
   // 准备两个变量, 赋值1和2
   x, y := 1, 2                                                       //变量的初始化
   // 交换变量值
   swapChange(&x, &y)                                             //函数调用
   // 输出变量值
   fmt.Println(x, y)                                               //语句&表达式输出
}

主函数: func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。函数执行开始后,函数内语句依照main()函数内依次执行

细化分析:

// 准备两个变量, 赋值1和2
   x, y := 1, 2                                                       //变量的初始化

变量的初始化: 在Go语言中给变量的赋值可以分为三个阶段,先创建变量,在内存中开辟空间后再初始化变量,将变量初始化为underfined,最后再进行真正赋值。在简化赋值的过程中直接使用:=(推导类型声明法)进行赋值,编译器能根据右值类型进行自主判断数据类型。

 // 交换变量值
   swapChange(&x, &y)                                             //函数调用

函数调用: 依据封装的指针交换函数,所以处理的参数是交换整型变量的指针当主函数运行到此语句时,则会调用指针交换函数进行数据的相关处理。

 // 输出变量值
   fmt.Println(x, y)

语句&表达式输出:fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。函数内语句依照main函数内依次执行。

3. 交换函数方法分析:

func swapChange(a, b *int) {                                     // 交换函数
 }

交换函数:通过函数中的赋值交换方法,将交换函数接收的数据进行处理,在此过程中完全在函数内部,换言之在函数中的数据处理过程不会受到外界影响。

 // 取a指针的值, 赋给临时变量t
   t := *a                                                           // 交换过程
   // 取b指针的值, 赋给a指针指向的变量
   *a = *b
   // 将a指针的值赋给b指针指向的变量
   *b = t

交换过程:在该交换过程中遵行的方式依然和一般标准变量类型的方式一样,如整型的赋值交换。通过一个临时变量去获取某一个指针A所指向的内存值,再将另一个指针B所指向的值地址赋值给该指针A,使该指针A获得指针B指向的内存地址,从而获取新的指向值。同时将临时变量获取的指针A所指向的内存值传到指针B所指的内存地址上。最终完成了数据的交换。

交换过程的实质依旧是赋值交换。