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

news2024/11/24 6:46:21

在这里插入图片描述

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

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

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

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

在这里插入图片描述

文章目录

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

Go语言中的指针

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

一、指针的基本概念

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

var p *int // 声明一个指向int型变量的指针

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

1. 获取变量的地址

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

var x int = 10
var p *int = &x // p存储了x的地址

2. 访问指针指向的值

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

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

二、指针的基本用法

1. 定义指针

在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函数用于创建一个指定类型的零值指针变量。例如:

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

在这里插入图片描述

3. 指针作为函数参数

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

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. 指针作为函数返回值

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

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值来表示空指针。例如:

package main

import "fmt"

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

在这里插入图片描述

三、指针的高级用法

1. 指针数组和指针切片

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

指针数组
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])
}

在这里插入图片描述

指针切片
slice := []int{4, 5, 6}
var pSlice *[]int = &slice
fmt.Println("Pointer to Slice:", *pSlice)

2. 数组指针

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

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函数),切片的底层数组可能会改变,此时原有的切片指针将不再指向新的底层数组。

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

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

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))

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语言中的指针有了更深入的了解。在实际开发中,可以根据项目需求,在合适的场景中使用指针来优化程序性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2160940.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈提示工程之In-context learning技术

提示工程之In-context learning技术; 通过一张图片围绕下边几个方面进行简单说明 概念起因本质结构注意事项 日常总结

SQL语法学习与实战应用

第一章 引言 1.1 MySQL数据库概述 MySQL,作为一种广泛使用的关系型数据库管理系统,自其问世以来,便凭借开源、高性能及低成本等显著特点,迅速占据了广泛的市场份额。这一系统不仅支持大规模并发访问,更提供了多样化的…

【最新华为OD机试E卷-支持在线评测】绘图机器(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…

【ARM】MDK-当选择AC5时每次点击build都会全编译

1、 文档目标 解决MDK中选择AC5时每次点击build都会全编译 2、 问题场景 在MDK中点击build时,正常会只进行增量编译,但目前每次点击的时候都会全编译。 3、软硬件环境 1 软件版本:Keil MDK 5.38a 2 电脑环境:Window 10 4、解决…

新手操作指引:快速上手腾讯混元大模型

引言 腾讯混元大模型是一款功能强大的AI工具,适用于文本生成、图像创作和视频生成等多种应用场景。对于新手用户,快速上手并充分利用这一工具可能会有些挑战。本文将提供详细的新手操作指引,帮助您轻松开始使用腾讯混元大模型。 步骤一&…

kubernetes网络(二)之bird实现节点间BGP互联的实验

摘要 上一篇文章中我们学习了calico的原理,kubernetes中的node节点,利用 calico 的 bird 程序相互学习路由,为了加深对 bird 程序的认识,本文我们将使用bird进行实验,实验中实现了BGP FULL MESH模式让宿主相互学习到对…

个人行政复议在线预约系统开发+ssm论文源码调试讲解

第二章 开发工具及关键技术介绍 2.1 JAVA技术 Java主要采用CORBA技术和安全模型,可以在互联网应用的数据保护。它还提供了对EJB(Enterprise JavaBeans)的全面支持,java servlet API,JSP(java server pages…

Pygame中Sprite实现逃亡游戏2

在《Pygame中Sprite实现逃亡游戏1》中实现了奔跑的玩家,接下来实现显示追赶玩家的飞龙以及对面过来的飞火。 1 显示飞龙 显示飞龙的代码如图1所示。 图1 显示飞龙的代码 其中,第93行代码创建了精灵类MySprite的实例dragon;第94行代码导入飞…

《十年国庆游,洞察中国旅游新趋势》

作者:侯炯 一、十年国庆旅游数据总览 过去十年,中国国庆旅游市场呈现出丰富的变化和强劲的发展态势。从接待游客人次来看,2014 年接待国内游客 4.75 亿人次,到 2019 年已增长至 7.82 亿人次,2023 年国内旅游出游人数更…

如何使用ssm实现新媒体视域下的中国古诗词展演+vue

TOC ssm678新媒体视域下的中国古诗词展演vue 绪论 课题背景 身处网络时代,随着网络系统体系发展的不断成熟和完善,人们的生活也随之发生了很大的变化。目前,人们在追求较高物质生活的同时,也在想着如何使自身的精神内涵得到提…

SpringBoot文档管理系统:架构与功能

第2章相关技术 2.1 Java技术介绍 Java语言擅长开发互联网类应用和企业级应用,现在已经相当的成熟,而且也是目前使用最多的编程语言之一。Java语言具有很好的面向对象性,可以符合人的思维模式进行设计,封装是将对象的属性和方法尽可…

FileLink:企业级跨网文件交换解决方案,效率与安全并存

在数字化转型的时代,企业面临着日益增长的文件交换需求。尤其是在跨网环境中,如何高效、安全地共享文件,成为企业运营的关键。FileLink 正是针对这一需求而生,为企业提供了一个高效、安全的文件交换解决方案。 一、FileLink的核心…

基本定时器的预分频器和技术周期的计算

从表中可见APB1和APB2他们的总线频率和时钟频率则是不一样的 APB1的总线频率是42MHZ 定时器的时钟频率则为84MHZ APB2的总线频率则为84MHZ 定时器则为168MHZ 如我们要使用某个寄存器则我们需要了解他们的定时器的频率则为多少 了解后则进行计算所需要的时间 列如:配置定时…

【CSS in Depth 2 精译_032】5.4 Grid 网格布局的显式网格与隐式网格(上)

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对…

揭秘隐世秘学与千门八将的智慧,为什么说是你人生必学?

引言 在浩瀚的人类文化长河中,隐藏着无数神秘的隐世秘学,它们或源于古老的传说,或深植于民间的智慧之中。这些秘学不仅承载着人类对未知世界的探索与想象,更蕴含着丰富的哲理与策略。其中,“千门八将”以其独特的智慧体…

ITU标准引领车内通讯新纪元

在现代汽车科技更迭的今天,车内通讯与免提通话系统的性能与稳定性成为了消费者购车时不可忽视的重要因素。随着国际电信联盟(ITU)一系列标准的推出,车内通讯体验正迈向新的高度。本文将深入探讨ITU-T P.1100、P.1110、P.1120、P.1…

3D建模:Agisoft Metashape Professional 详细安装教程分享 Mac/win

Agisoft Metashape中文版(以前称为 PhotoScan)是一款独立软件产品,可对数字图像进行摄影测量处理并生成 3D 空间数据,用于 GIS 应用程序、文化遗产文献和视觉效果制作以及各种比例的物体的间接测量。 明智地实施数字摄影测量技术…

Qt/C++ 多线程同步机制详解及应用

在多线程编程中,线程之间共享资源可能会导致数据竞争和不一致的问题。因此,采用同步机制确保线程安全至关重要。在Qt/C中,常见的同步机制有:互斥锁(QMutex、std::mutex)、信号量(QSemaphore&…

数据结构--单链表创建、增删改查功能以及与结构体合用

一、作业要求 单链表操作,要求节点是结构体类型,实现以下功能: 1.尾插学生 2.任意位置插入学生 3.任意位置删除学生 4.逆置单链表 5.学生按学号排序 6.销毁单链表 二、实现过程 1.代码如下: (1)头…

scanning folder for git repositories 当前没有源代码管理提供程序进行注册

这个问题困扰了我好几天。尝试了各种方法,虽然有了解决方法 。但是感觉根本原因还是没找到。解决方案是更改git的 openRepositoryInParentFolders 为always 。我之所以觉着没找到根本原因是因为 我远程另一个主机仍然使用prompt 确是正常的。 解决方案原文 https://…