【Go语言】深入解读Go语言中的指针,助你拨开迷雾见月明

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑

🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。

🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏: Go语言开发零基础到高阶实战
景天的主页: 景天科技苑

文章目录

  • Go语言中的指针
    • 一、指针的基本概念
      • [1. 获取变量的地址](#1. 获取变量的地址)
      • [2. 访问指针指向的值](#2. 访问指针指向的值)
    • 二、指针的基本用法
      • [1. 定义指针](#1. 定义指针)
      • [2. 使用`new`函数创建指针](#2. 使用new函数创建指针)
      • [3. 指针作为函数参数](#3. 指针作为函数参数)
      • [4. 指针作为函数返回值](#4. 指针作为函数返回值)
      • [5. 空指针检查](#5. 空指针检查)
    • 三、指针的高级用法
      • [1. 指针数组和指针切片](#1. 指针数组和指针切片)
      • [2. 数组指针](#2. 数组指针)
      • [3. 指针和切片](#3. 指针和切片)
      • [4. 指针和方法](#4. 指针和方法)
      • [5. 接口中的指针](#5. 接口中的指针)
      • [6. 指针的指针](#6. 指针的指针)
    • 四、总结

Go语言中的指针

在Go语言中,指针是一种特殊的变量类型,它用于存储变量的内存地址。通过指针,程序可以直接访问和修改变量的值,这在处理大型数据结构、优化内存使用和提高程序性能时非常有用。本文将结合具体案例,详细讲解Go语言中指针的用法,包括定义指针、使用指针访问和修改变量值、指针作为函数参数和返回值、指针数组和切片、结构体指针、空指针检查等。

一、指针的基本概念

指针是指向内存地址的变量。在Go语言中,使用*操作符来声明一个指针变量。例如:

go 复制代码
var p *int // 声明一个指向int型变量的指针

这里p是一个整型指针,它存储的是一个整型变量的内存地址。

1. 获取变量的地址

通过在变量名前加上&符号,可以获取变量的内存地址。例如:

go 复制代码
var x int = 10
var p *int = &x // p存储了x的地址

2. 访问指针指向的值

通过在指针变量前加上*符号,可以访问指针指向的值。例如:

go 复制代码
fmt.Println(*p) // 输出指针p指向的值,即变量x的值

二、指针的基本用法

1. 定义指针

在Go语言中,使用*操作符定义指针变量。例如:

go 复制代码
package main

import "fmt"

func main() {
	var x int = 42
	var p *int = &x // 定义一个int类型的指针变量p,并将x的地址赋值给p
	//获取指针指向的地址中存的值
	fmt.Println(*p)
}

2. 使用new函数创建指针

new函数用于创建一个指定类型的零值指针变量。例如:

go 复制代码
p := new(int) // 创建一个int类型的零值指针变量
*p = 42 // 通过指针设置值
fmt.Println("Value stored in p:", *p)

3. 指针作为函数参数

将指针作为函数参数,可以在函数内部修改原始变量的值,从而避免函数对变量进行拷贝,提高程序的性能。例如:

go 复制代码
func changeValue(a *int) {
    *a = 20
}

func main() {
    x := 10
    fmt.Println("Before:", x)
    changeValue(&x)
    fmt.Println("After:", x)
}

输出结果为:

复制代码
Before: 10
After: 20

这表明我们通过指针修改了x的值。

4. 指针作为函数返回值

函数可以返回指针类型的值,以便在函数外部访问函数内部创建的变量。

go 复制代码
package main

import "fmt"

// 指针函数, 指针是可以用作函数的返回值
func main() {
	// 调用了这个函数后,可以得到一个指针类型的变量。
	ptr := f5()

	//内存地址正常打印前面带个&
	fmt.Println("ptr:", ptr)
	fmt.Printf("ptr:%p\n", ptr)
	fmt.Printf("ptr类型:%T\n", ptr)
	fmt.Println("ptr的地址:", &ptr)
	fmt.Println("ptr地址中的值:", *ptr)

	// 使用
	fmt.Println((*ptr)[0])
	ptr[0] = 10
	fmt.Println(ptr[0])

}

// 调用该函数后返回一个指针,此时返回个数组指针
func f5() *[4]int {
	arr := [4]int{1, 2, 3, 4}
	//内存地址往往被赋值给指针
	return &arr

}

5. 空指针检查

在使用指针之前,应该进行空指针检查,以避免出现空指针引用的错误。可以使用nil值来表示空指针。例如:

go 复制代码
package main

import "fmt"

func main() {
	var p *int // 声明一个int类型的指针变量,默认值为nil
	if p != nil {
		fmt.Println(*p) // 对非空指针进行操作
	} else {
		fmt.Println("Pointer is nil")
	}
}

三、指针的高级用法

1. 指针数组和指针切片

指针数组和指针切片允许我们存储多个指针变量,并通过这些指针访问和修改对应的值。

指针数组
go 复制代码
package main

import "fmt"

func main() {

	a := 1
	b := 2
	c := 3
	d := 4

	// 创建一个指针数组
	arr1 := [4]*int{&a, &b, &c, &d}
	fmt.Println(arr1)

	// 通过指针修改a的值
	// arr1[0] 0xc00000e0a8
	*arr1[0] = 100
	fmt.Println(a)

	a = 200
	fmt.Println(*arr1[0])
}
指针切片
go 复制代码
slice := []int{4, 5, 6}
var pSlice *[]int = &slice
fmt.Println("Pointer to Slice:", *pSlice)

2. 数组指针

数组指针,首先应该是一个指针,指向了一个数组

go 复制代码
package main

import "fmt"

// 数组指针
func main() {

	// 创建数组,值传递。fun
	arr1 := [4]int{1, 2, 3, 4}
	fmt.Println("arr1:", arr1)
	fmt.Printf("arr1指向的地址:%p\n", &arr1)

	// 创建一个指针,指向这个数组的地址,通过指针来操作数组
	//var p1 *[4]int
	//创建指针方法二
	p1 := new([4]int)
	p1 = &arr1
	fmt.Printf("p1指向的地址: %p\n", p1)
	fmt.Printf("p1自己的地址: %p\n", &p1)
	fmt.Println("p1指向的地址的值: ", *p1)
	fmt.Printf("p1指向的地址的值: %v\n", *p1)

	// 操作数组指针 来修改数组
	(*p1)[0] = 100 // 原生写法
	fmt.Println("arr1:", arr1)
	fmt.Println("p1指向的地址的值: ", *p1)

	// 语法糖:由于p1指向了arr1这个数组,所以可以直接用p1来操控数组
	// 指向了谁,这个指针就可以代表谁。
	// p1 = arr1
	p1[0] = 200 // 在程序中,我们更多时候是这样在使用指针的
	fmt.Println("arr1:", arr1)
	fmt.Println("p1指向的地址的值: ", *p1)

}

3. 指针和切片

在Go语言中,切片本身就是通过指针引用的,因此修改切片元素的值也会影响到原始数组。但是,当重新分配切片时(例如使用append函数),切片的底层数组可能会改变,此时原有的切片指针将不再指向新的底层数组。

go 复制代码
func modifySlice(s *[]int) {
    (*s)[0] = 99
    *s = append(*s, 100)
}

func main() {
    slice := []int{1, 2, 3}
    fmt.Println("Original Slice:", slice)
    modifySlice(&slice)
    fmt.Println("Modified Slice:", slice)
}

4. 指针和方法

在Go语言中,方法的接收者可以是值类型或指针类型。使用指针作为接收者可以减少数据拷贝,提高性能,特别是当处理大型结构体时。

go 复制代码
type Rectangle struct {
    Width  int
    Height int
}

func (r *Rectangle) Area() int {
    return r.Width * r.Height
}

func main() {
    rect := Rectangle{Width: 5, Height: 10}
    fmt.Println("Area:", rect.Area())
}

5. 接口中的指针

在Go语言中,接口是一种类型,它定义了对象的行为。当接口的实现是大型结构体时,使用指针作为接口的实现可以减少数据拷贝,提高性能。

go 复制代码
type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c *Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func printArea(s Shape) {
    fmt.Println("Area:", s.Area())
}

func main() {
    circle := Circle{Radius: 5}
    printArea(&circle) // 注意这里传递的是&circle,因为Circle的Area方法接收的是指针
}

注意:在上面的printArea函数调用中,我们传递了&circle而不是circle,因为CircleArea方法接收的是*Circle类型的参数。

6. 指针的指针

指针的套娃,指针指向指针 , 指针类型 第一个*指针类型, *int是这个指针对应的类型。

如何理解多个符号,第一个取出来后,后面就是它存的类型 *(*(int))

go 复制代码
package main

import "fmt"

func main() {

	// 声明 普通变量
	var a int = 10

	// 声明 指针变量,指向a, 指针其实就是一个特殊的变量而已。,ptr命名  p
	// 定义变量格式  var ptr *类型
	var p *int
	p = &a // 指针变量赋值

	// 指针的套娃,指针指向指针 , 指针类型 第一个*指针类型, *int是这个指针对应的类型
	// 如何理解多个符号,第一个取出来后,后面就是它存的类型 *(*(int))
	var ptr **int
	ptr = &p
	//
	fmt.Printf("ptr变量存储的指针的地址:%p\n", ptr) //就是p的地址
	fmt.Printf("ptr变量自己的地址:%p\n", &ptr)
	fmt.Printf("*ptr变量存储的地址:%p\n", *ptr)     //就是p存的指针的地址
	fmt.Printf("*ptr变量存储的地址中的值:%d\n", **ptr) //就是p存的指针的地址指向的值,即是a的值
	// 修改变量a就有了无数种方式
	**ptr = 1111
	fmt.Println(a)
}

四、总结

指针在Go语言中是一种非常强大的工具,它允许我们直接访问和修改内存中的值,优化内存使用和提高程序性能。然而,过度使用指针也可能导致代码难以理解和维护,还可能引发内存泄漏和悬空指针等问题。因此,在使用指针时要谨慎,并遵循Go语言的指针使用规范。

通过本文的详细讲解和具体案例,相信读者已经对Go语言中的指针有了更深入的了解。在实际开发中,可以根据项目需求,在合适的场景中使用指针来优化程序性能。

相关推荐
JavaGuide1 分钟前
今年小红书后端开出了炸裂的薪资!
后端·面试
嵌入式-老费4 分钟前
自己动手写深度学习框架(快速学习python和关联库)
开发语言·python·学习
ctgu9011 分钟前
PyQt5(八):ui设置为可以手动随意拉伸功能
开发语言·qt·ui
L.EscaRC13 分钟前
Redisson在Spring Boot中的高并发应用解析
java·spring boot·后端
CVer儿19 分钟前
libtorch ITK 部署 nnUNetV2 模型
开发语言
苏三的开发日记22 分钟前
MySQL事务隔离级别及S与X锁
后端
阑梦清川26 分钟前
claude全面封杀国产IDE,trae已经无法使用claude大模型了
后端
asyxchenchong88827 分钟前
OpenLCA、GREET、R语言的生命周期评价方法、模型构建
开发语言·r语言
lzptouch33 分钟前
Django项目
后端·python·django
没有梦想的咸鱼185-1037-166339 分钟前
【生命周期评价(LCA)】基于OpenLCA、GREET、R语言的生命周期评价方法、模型构建
开发语言·数据分析·r语言