GO语言学习(11)指针

目录

🐭前言

1.什么是指针🌳

2.使用指针🌳

new函数 🌸

3.空指针🌳 

4.指针数组 🌳 

 5.指向指针的指针🌳 

6.函数的引用传递🌳


🐭前言

        Go 语言的指针是一种直接操作内存地址的机制,但相较于 C/C++,Go 的指针设计更加安全和简洁,避免了常见的内存错误(如野指针、悬垂指针)。

1.什么是指针🌳

        我们都知道,变量 是一种使用方便的占位符,用于引用计算机内存地址。Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。而指针是一个特殊的变量,用于存储了另一个变量的内存地址。具体原理可参考第2节当中的 代码示例

2.使用指针🌳

        指针的基本使用语法如下:

var 指针名 *数据类型

        🌶 例如:

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

        使用 & 操作符获取变量的地址,使用 * 操作符访问指针指向的值。下面请看一段 示例代码 具体了解指针的原理。

package main
import "fmt"

func main() {
	a := 10
	var p *int
	fmt.Println("变量a的内存地址:", &a)
	p = &a
	fmt.Println("指针p存储的内容:", p)
	fmt.Println("指针p所指向内存地址的元素值", *p)
}

上述代码最终输出结果如下:

变量a的内存地址: 0xc00000a0f8
指针p存储的内容: 0xc00000a0f8
指针p所指向内存地址的元素值 10

🍒 解释:

  • 计算机当中的内存空间可以被划分为一个个小方块,每一个小方块都对应一个地址。这就好比一栋小区楼房,每家每户都有属于自己的独一无二的门牌号。

  • 当执行语句 a := 10 时,就好比有人搬家入住楼房,入住之后,他就会获得属于自己家的门牌号。计算机会为变量 a 随机分配一个空闲的内存空间的地址,将10放入该地址空间中。

          所以此时,我们用取址符 & 访问变量a的地址,输出就为 0xc00000a0f8。

  • 由于 a 是int类型,因此当我们想用指针指向变量 a 时,指针的数据类型也得是int,讲究“门当户对”,由于指针的本质是存储变量的地址,所以我们执行语句  p = &a ,给指针变量p赋值,此时指针p存储的值就是变量a的地址。

        所以此时,指针p的存储值就是变量a的地址,输出就为 0xc00000a0f8。

  •  而 * 操作符的作用就是解引用指针,就好比依据别人家的门牌号,上门拜访。*p 的本质就是访问指针p所指向的内存空间中对应地址所存储的值

new函数 🌸

        new(T) 分配内存并返回指向该类型的指针,初始值为对应数据类型的默认值。例如:

p := new(int) // p 是 *int 类型,指向的 int 值为 0

3.空指针🌳 

        当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。空指针的判断可用如下语句:

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

4.指针数组 🌳 

        在 Go 语言中,指针数组是一个数组,其元素类型为指针(即每个元素存储的是另一个变量的内存地址)。与普通数组不同,指针数组的每个元素指向内存中的某个值,而不是直接存储值本身。这种结构常用于高效操作多个变量的内存地址,或在函数间共享数据。

// 声明一个长度为3的指针数组,每个元素是 *int 类型
var ptrArr [3]*int

         注意:指针数组元素默认初始值为 nil,此时使用 * 解引用会触发 panic 错误。

        指针数组需要为每个元素分配指向具体变量的地址。初始化方式有以下几种:

(1) 直接赋值地址:

a, b, c := 10, 20, 30
ptrArr := [3]*int{&a, &b, &c}

(2) 使用 new 分配内存:

var ptrArr [3]*int
for i := 0; i < 3; i++ {
    ptrArr[i] = new(int)  // 为每个元素分配内存
    *ptrArr[i] = i * 10   // 赋值
}

         指针数组的访问与修改,通常需要 * 解引符参与,用于获取对应地址所存储的元素。

fmt.Println(*ptrArr[0]) // 输出第一个元素指向的值
*ptrArr[1] = 100  // 修改第二个元素指向的值

// 可以更换指针的指向目标
d := 200
ptrArr[2] = &d    // 让第三个元素指向新的变量 d

        通过指针数组传递内存地址,避免值拷贝:

func process(arr [3]*int) {
    for i := 0; i < len(arr); i++ {
        *arr[i]++  // 修改原数据
    }
}

func main() {
    a, b, c := 1, 2, 3
    ptrArr := [3]*int{&a, &b, &c}
    process(ptrArr)
    fmt.Println(a, b, c) // 输出 2, 3, 4
}

 注意以下是一个典型的错误例子,使用指针时,请确保确保指针指向的内存未被提前释放:

func createArray() [2]*int {
    var arr [2]*int
    for i := 0; i < 2; i++ {
        val := i * 10  // val 是局部变量,函数返回后内存失效
        arr[i] = &val  // 错误!悬垂指针
    }
    return arr
}

        Go 的数组是固定长度的,无法动态扩展。若需动态长度,应使用切片。也就是指针切片,用法与指针数组相似,这里不过多介绍,关于切片的介绍,具体可看:Go语言切片 

 5.指向指针的指针🌳 

        如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址。

        声明格式如下:

var 指针名 **数据类型;

 🍊示例代码:

package main

import "fmt"

func main() {

   var a int
   var ptr *int
   var pptr **int

   a = 3000

   /* 指针 ptr 地址 */
   ptr = &a

   /* 指向指针 ptr 地址 */
   pptr = &ptr

   /* 获取 pptr 的值 */
   fmt.Printf("变量 a = %d\n", a )
   fmt.Printf("指针变量 *ptr = %d\n", *ptr )
   fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr)
}

上述代码最终输出结果如下:

变量 a = 3000
指针变量 *ptr = 3000
指向指针的指针变量 **pptr = 3000

6.函数的引用传递🌳

        具体用法可参见:GO语言学习-函数 当中第三章中的引用传递部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值