Golang 指针使用教程

文章目录

  • Go语言特性
  • Go语言指针
  • Go指针示例
  • Go应用场景

Go语言特性

  • Go函数只有 值传递
  • Go指针 不能运算
  • 指针类型的定义与基础数据类型有关,即指针类型是与其指向的变量的类型相关联的。

Go语言指针

使用new函数: new函数用于分配内存,并返回一个指向该类型的新零值的指针。

  • new(int)分配了一个int类型的内存,并返回一个指向该内存的指针。
package mainimport "fmt"func main() {var p *intp = new(int)fmt.Println(*p, p, &p) // 输出: 0 0xc00009e018 0xc0000a4018
}

取地址符号(&): 取地址符号&可以获取一个变量的内存地址。

  • &a获取变量a的内存地址,并将其赋值给指针变量p。
package mainimport "fmt"func main() {var a int = 42var p *int = &afmt.Println(*p, p, &p) // 输出: 42 0xc000112008 0xc000108018
}
  • *p 是指针p所指向的变量的值,即a的值,输出42。
  • p 是指针变量,存储的是变量a的内存地址,这个地址可能是像 0xc000112008 这样的值(实际地址在每次运行时可能会不同)。
  • &p 是指针变量p本身的内存地址,因为p是定义在函数栈上的一个变量,所以它也有自己的内存地址,类似 0xc000108018 这样的值(实际地址在每次运行时可能会不同)。

使用*操作符: 指针也可以直接通过*操作符进行解引用或指向具体的值。

  • *p = 42将值42赋值给指针p所指向的内存地址。
package mainimport "fmt"func main() {var p *intp = new(int)*p = 42fmt.Println(p, *p, &p)   // 输出:0xc00009e018 42 0xc0000a4018
}

作为函数参数: 函数参数也可以是指针类型,允许在函数内修改变量的值。Go语言函数只有值传递,所以常用指针作为函数参数。

package mainimport "fmt"func setToZero(p *int) {*p = 0
}func main() {var x int = 5setToZero(&x)// x 现在是 0fmt.Println(x) // 输出:0
}

指向结构体的指针: 结构体(struct)是用户定义的类型,用于将一组数据组合在一起。指向结构体的指针在处理复杂数据和需要修改结构体字段的场景中非常有用。

package mainimport "fmt"func main() {type Person struct {Name stringAge  int}var person Person = Person{Name: "Alice", Age: 30}var pPerson *Person = &personfmt.Println(pPerson)   // 输出: &{Alice 30}fmt.Println(*pPerson)  // 输出: {Alice 30}fmt.Println(&pPerson)  // 输出: 0xc000054020
}

作为函数返回值: 返回指针可以用于创建和返回函数内部的变量,或者用于修改调用者传入的数据。使用指针能够提高性能,避免不必要的值拷贝,但需要注意避免悬空指针和内存泄漏等问题。

package mainimport "fmt"// 定义一个返回指针的函数
func createIntPointer(value int) *int {// 在函数内部创建一个变量v := value// 返回该变量的指针return &v
}func main() {// 调用返回指针的函数p := createIntPointer(42)// 打印指针和值fmt.Println("指针:", p) // 输出: 指针: 0xc0000121a8fmt.Println("值:", *p)  // 输出: 值: 42
}
  • 返回结构体指针是一种常见的实践,尤其是在需要创建新的结构体实例并将其返回给调用者时。这样可以避免结构体的值拷贝,直接操作内存中的数据,提高效率。
package mainimport "fmt"// 定义一个结构体类型
type Person struct {Name stringAge  int
}// 定义一个返回结构体指针的函数
func NewPerson(name string, age int) *Person {// 创建结构体实例person := Person{Name: name,Age:  age,}// 返回结构体的指针return &person
}func main() {// 调用返回结构体指针的函数p := NewPerson("Alice", 30)// 访问和修改结构体字段fmt.Println("Name:", p.Name) // 输出: Name: Alicefmt.Println("Age:", p.Age) // 输出: Age: 30// 修改结构体字段p.Age = 31fmt.Println("Updated Age:", p.Age) // 输出: Updated Age: 31
}

结构体中的指针字段: 结构体字段可以是指针类型,这使得我们可以更灵活地操作复杂的数据结构。

package mainimport "fmt"type Node struct {Value intNext  *Node
}func main() {node1 := &Node{Value: 1}node2 := &Node{Value: 2}node1.Next = node2fmt.Println(node1)      // 输出: &{1 0xc000014080}fmt.Println(node1.Next) // 输出: &{2 <nil>}
}

指向数组的指针: 数组的指针可以通过指向数组的第一个元素来获得。

package mainimport "fmt"func main() {var arr [3]int = [3]int{1, 2, 3}var p *[3]int = &arrfmt.Println(p)    // 输出: &[1 2 3]fmt.Println(*p)   // 输出: [1 2 3]fmt.Println(&p)   // 输出: 0xc000054020
}

指针数组: 指针数组是一个存储指针的数组,每个元素都是一个指向某类型的指针。

package mainimport "fmt"func main() {var arr [3]*inta, b, c := 1, 2, 3arr[0] = &aarr[1] = &barr[2] = &cfmt.Println(arr) // 输出: [0xc00009e018 0xc00009e020 0xc00009e028]
}

指向切片的指针: 切片(slice)是一种动态数组,可以在程序运行时动态调整大小。虽然切片本身已经是一个引用类型,通常情况下不需要使用指向切片的指针,但在某些特殊场景下,指向切片的指针可以更高效或更简洁地处理一些操作。

package mainimport "fmt"func main() {slice := []int{1, 2, 3}// 定义一个指向切片的指针var p *[]int = &slice// 使用指向切片的指针修改切片*p = append(*p, 4, 5, 6)// 输出切片内容fmt.Println(slice)     // 输出: [1 2 3 4 5 6]fmt.Println(p, *p, &p) // 输出: &[1 2 3 4 5 6] [1 2 3 4 5 6] 0xc000054020
}

指向映射的指针: 映射(map)是一种内置的数据结构,用于存储键值对。与切片类似,映射本质上是引用类型,因此在大多数情况下,直接传递映射就足够了。然而,在某些特定的场景下,使用指向映射的指针(map pointer)可以提供更高的灵活性和性能优化。

package mainimport "fmt"func main() {// 定义一个映射m := map[string]int{"one": 1, "two": 2}// 定义一个指向映射的指针var p *map[string]int = &m// 使用指向映射的指针修改映射(*p)["three"] = 3// 输出映射内容fmt.Println(m) // 输出: map[one:1 three:3 two:2]fmt.Println(p, *p, &p) // 输出: &map[one:1 three:3 two:2] map[one:1 three:3 two:2] 0xc000054028
}

指向函数的指针: 函数也是一种类型,因此可以创建指向函数的指针。这种功能在实现回调机制、函数作为参数传递以及动态调用函数等场景中非常有用。

package mainimport "fmt"// 定义一个类型为函数指针的回调函数
type Callback func(int) int// 应用回调函数的函数
func applyCallback(x int, callback Callback) int {return callback(x)
}// 回调函数示例
func double(n int) int {return n * 2
}func main() {result := applyCallback(5, double)fmt.Println(result, &result) // 输出: 10 0xc00009e018fmt.Println(double) // 输出: 0x5558980
}

Go指针示例

package mainimport "fmt"type Person struct {Name stringAge  int
}func add(a, b int) int {return a + b
}func main() {var pInt *intpInt = new(int)fmt.Println("整型指针:", &pInt, *pInt, pInt)var pString *stringpString = new(string)fmt.Println("字符串指针:", &pString, *pString, pString)var pArr *[2]intpArr = new([2]int)fmt.Println("数组指针:", &pArr, *pArr, pArr)var pSlice *[]intpSlice = new([]int)fmt.Println("切片指针:", &pSlice, *pSlice, pSlice)slice := []int{1, 2, 3}var pointSlice *[]int = &slicefmt.Println("切片指针:", &pointSlice, *pointSlice, pointSlice)var person Person = Person{Name: "Alice", Age: 20}var pPerson *Person = &personfmt.Println("结构体指针:", &pPerson, *pPerson, pPerson)fmt.Println("函数指针:", add)
}
  • 输出结果
整型指针: 0xc0000a6010 0 0xc00009e018
字符串指针: 0xc0000a6020  0xc000090020
数组指针: 0xc0000a6028 [0 0] &[0 0]
切片指针: 0xc0000a6030 [] &[]
切片指针: 0xc0000a6038 [1 2 3] &[1 2 3]
结构体指针: 0xc0000a6040 {Alice 20} &{Alice 20}
函数指针: 0xeecb980

Go应用场景

函数参数传递: 当函数参数是一个较大的结构体或数组时,传递指针而不是副本可以显著提高性能并节省内存。

package mainimport ("fmt"
)type LargeStruct struct {Field1 [1024]intField2 [1024]int
}func modifyLargeStruct(ls *LargeStruct) {ls.Field1[0] = 999
}func main() {ls := LargeStruct{}modifyLargeStruct(&ls)fmt.Println(ls.Field1[0]) // 输出: 999
}

共享内存: 在多线程或并发编程中,指针用于共享数据和同步状态。

package mainimport ("fmt""sync"
)func increment(counter *int, wg *sync.WaitGroup) {*counter++wg.Done() // 完成时调用Done
}func main() {var counter intvar wg sync.WaitGroup // 用于等待一组goroutines完成执行的同步机制for i := 0; i < 1000; i++ {wg.Add(1) // 增加一个需要等待的goroutine计数go increment(&counter, &wg)}wg.Wait() // 等待所有goroutine完成fmt.Println("Counter:", counter) // 输出: Counter: 989
}

实现链表等复杂数据结构: 链表、树等数据结构的实现通常需要使用指针来链接节点。

package mainimport ("fmt"
)type Node struct {Value intNext  *Node
}func main() {head := &Node{Value: 1}second := &Node{Value: 2}third := &Node{Value: 3}head.Next = secondsecond.Next = thirdfor n := head; n != nil; n = n.Next {fmt.Println(n.Value)}
// 输出:
// 1
// 2
// 3
}

修改变量值: 通过指针,可以在函数中修改传入变量的值,而不仅仅是函数内部的局部副本。

package mainimport ("fmt"
)func setToZero(x *int) {*x = 0
}func main() {a := 5setToZero(&a)fmt.Println(a) // 输出: 0
}

减少内存分配: 在高性能场景中,频繁的内存分配和释放会影响性能,指针可以帮助减少这种开销。

package mainimport ("fmt"
)type Item struct {Value int
}func processItem(item *Item) {item.Value = 42
}func main() {items := make([]Item, 5)for i := range items {processItem(&items[i])}fmt.Println(items) // 输出: [{42} {42} {42} {42} {42}]
}

实现回调函数: 指针用于传递函数指针,实现回调机制。

package mainimport ("fmt"
)func apply(f func(int) int, x *int) {*x = f(*x)
}func double(n int) int {return n * 2
}func main() {x := 5apply(double, &x)fmt.Println(x) // 输出: 10
}

内存映射文件(mmap): 在操作系统支持内存映射文件的情况下,指针可以用于直接访问文件内容,提高IO操作效率。

  • example.txt
Golang
import ("os""fmt""syscall"
)func main() {f, err := os.OpenFile("example.txt", os.O_RDWR, 0644)if err != nil {panic(err)}defer f.Close()data, err := syscall.Mmap(int(f.Fd()), 0, 4096, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)if err != nil {panic(err)}defer syscall.Munmap(data)// 修改内存映射区域的数据data[0] = 'H'data[1] = 'i'fmt.Println(string(data))  // 输出: Hilang
}

高效的缓存和池管理: 指针用于实现对象池,以复用对象,减少GC压力。

package mainimport ("fmt""sync"
)var pool = sync.Pool{New: func() interface{} {return &Object{}},
}type Object struct {Value int
}func main() {obj := pool.Get().(*Object)obj.Value = 42pool.Put(obj)anotherObj := pool.Get().(*Object)fmt.Println(anotherObj.Value) // 输出: 42
}

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

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

相关文章

SpringBoot 参数验证的几种方式

文章目录 SpringBoot 参数验证1、为什么要进行参数验证2、验证方式2.1 if 语句判断2.2 Assert2.3 Validator2.3.1 引入依赖2.3.2 定义参数实体类2.3.4 定义特定异常全局拦截方法2.3.5 定义校验类进行测试2.3.6 测试 2.4 自定义验证注解2.4.1 定义自定义注解2.4.2 定义自定义验证…

Python第二语言(八、Python包)

目录 1. 什么是Python包 2. 创包步骤 2.1 new包 2.2 查看创建的包 2.3 拖动文件到包下 3. 导入包 4. 安装第三方包 4.1 什么是第三方包 4.2 安装第三方包-pip 4.3 pip网络优化 1. 什么是Python包 包下有__init__.py就是包&#xff0c;无__init__.py就是文件夹。于Ja…

嵌入式学习——Linux高级编程复习(进程)——day39

1. 进程 进程是计算机科学中的一个核心概念&#xff0c;它是操作系统进行资源分配和调度的基本单位&#xff0c;代表了一个正在执行中的程序实例。当一个程序被加载到内存并开始执行时&#xff0c;它就变成了一个进程。 1. 程序&#xff1a;存放在外存中的一段代码的集合 2. 进…

牛客 NC129 阶乘末尾0的数量【简单 基础数学 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/aa03dff18376454c9d2e359163bf44b8 https://www.lintcode.com/problem/2 思路 Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff…

Python 很好用的爬虫框架:Scrapy:

了解Scrapy 爬虫框架的工作流程&#xff1a; 在scrapy中&#xff0c; 具体工作流程是这样的&#xff1a; 首先第一步 当爬虫引擎<engine>启动后&#xff0c; 引擎会到 spider 中获取 start_url<起始url> 然后将其封装为一个request对象&#xff0c; 交给调度器<…

GDPU JavaWeb 大结局篇(持续更新中)

GDPUJavaWeb程序设计复习&#xff0c;习题集&#xff0c;重点知识总结&#xff0c;一篇就够了。 实验复习 JavaWeb代码复习&#xff0c;在专栏也可查阅。 课后巩固习题 1 【单选题】下列说法正确的是( D ) A、在B/S结构中,结果应用软件发生了改变,就必须通知所有的客户端重新…

Elastic Search 8.14:更快且更具成本效益的向量搜索,使用 retrievers 和重新排序提升相关性,RAG 和开发工具

作者&#xff1a;来自 Elastic Yaru Lin, Ranjana Devaji 我们致力于突破搜索开发的界限&#xff0c;并专注于为搜索构建者提供强大的工具。通过我们的最新更新&#xff0c;Elastic 对于处理以向量表示的大量数据的客户来说变得更加强大。这些增强功能保证了更快的速度、降低的…

Activity->Activity中动态添加Fragment->add和replace方式添加的区别

XML文件 Activity布局文件R.layout.activity_main <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:id"id/root_ll"android:orientation"v…

Linux本地搭建DataEase并发布公网远程访问进行数据分析

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

【RAG入门教程01】Langchian框架 v0.2介绍

LangChain 是一个开源框架&#xff0c;旨在简化使用大型语言模型 (LLM) 创建应用程序的过程。可以将其想象成一套使用高级语言工具进行搭建的乐高积木。 它对于想要构建复杂的基于语言的应用程序而又不必管理直接与语言模型交互的复杂性的开发人员特别有用。它简化了将这些模型…

我已经入驻@面包多平台

大学常见的Javaswing 图书管理系统已经真实发布&#xff0c;使用MySQL作为数据支撑&#xff0c;欢迎点击下面的链接随时购买。 面包多-JavaSwing MySQL图书管理系统

数据库之PostgreSQL详解

一、PostgreSQL介绍 PostgreSQL是一个功能强大的 开源 的关系型数据库。底层基于C实现。 PostgreSQL的开源协议和Linux内核版本的开源协议是一样的。。BDS协议&#xff0c;这个协议基本和MIT开源协议一样&#xff0c;说人话&#xff0c;就是你可以对PostgreSQL进行一些封装&a…

如何在本地和远程删除 Git 分支

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;目前是武汉城市开发者社区主理人 擅长.net、C、python开发&#xff0c; 如果遇…

【人工智能】ChatGPT基本工作原理

ChatGPT 是由 OpenAI 开发的一种基于深度学习技术的自然语言处理模型&#xff0c;它使用了名为 GPT&#xff08;Generative Pre-trained Transformer&#xff09;的架构。GPT 模型是一种基于 Transformer 架构的预训练语言模型&#xff0c;它通过大量的文本数据进行预训练&…

SpringBoot之Mybatis-plus实战

文章目录 MybatisPlus 介绍一、MyBatisPlus 集成步骤第一步、引入依赖第二步、定义mapper 二、注解TableNameTableldTableField 加解密实现步骤 在SpringBoot项目中使用Mybatis-plus&#xff0c;记录下来&#xff0c;方便备查。 MybatisPlus 介绍 为简化开发而生&#xff0c;官…

CSAPP Lab01——Data Lab完成思路

陪你把想念的酸拥抱成温暖 陪你把彷徨写出情节来 未来多漫长再漫长还有期待 陪伴你 一直到 故事给说完 ——陪你度过漫长岁月 完整代码见&#xff1a;CSAPP/datalab-handout at main SnowLegend-star/CSAPP (github.com) 01 bitXor 这道题是用~和&计算x^y。 异或是两个…

红黑树的介绍与实现

前言 前面我们介绍了AVL树&#xff0c;AVL树是一棵非常自律的树&#xff0c;有着严格的高度可控制&#xff01;但是正它的自律给他带来了另一个问题&#xff0c;即虽然他的查找效率很高&#xff0c;但是插入和删除由于旋转而导致效率没有那么高。我们上一期的结尾说过经常修改…

C语言:双链表

一、什么是双链表&#xff1f; 双链表&#xff0c;顾名思义&#xff0c;是一种每个节点都包含两个链接的链表&#xff1a;一个指向下一个节点&#xff0c;另一个指向前一个节点。这种结构使得双链表在遍历、插入和删除操作上都表现出色。与单链表相比&#xff0c;双链表不仅可以…

【机器学习】【遗传算法】【项目实战】药品分拣的优化策略【附Python源码】

仅供学习、参考使用 一、遗传算法简介 遗传算法&#xff08;Genetic Algorithm, GA&#xff09;是机器学习领域中常见的一类算法&#xff0c;其基本思想可以用下述流程图简要表示&#xff1a; &#xff08;图参考论文&#xff1a;Optimization of Worker Scheduling at Logi…

DVB-S系统发射端Matlab仿真及FPGA实现

DVB标准 Digital Video Broadcasting&#xff08;数字视频广播&#xff09;是一个完整的数字电视解决方案&#xff0c;其中包括DVB-C&#xff08;数字电视有线传输标准&#xff09;&#xff0c; DVB-T&#xff08;数字电视地面传输标准&#xff09;&#xff0c;DVB-S&#xff…