news 2026/4/24 14:02:36

go语言指针详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
go语言指针详解

两个常用的符号

  • &取地址符
  • *取指针存的地址中的内容(指针中存的是某个内存地址,用*指针变量取出的是这个地址中存的内容)

指针的概念

指针=内存地址如果指针没有限制,那它可以指向内存中的任何位置

a内存地址 0x12345678, 值:100 var a int = 100

*b指针变量,指向了一个内存地址 0x12345678(a的内存地址)

指针变量b中存了变量a的地址。这个就是b指向了a

varaint=100

varb= &a

fmt.Println("a本身的内存地址:", &a)//0xc00008c0a8

fmt.Println("b内存中存储的地址:",b)//0xc00008c0a8

fmt.Println("b本身的内存地址:", &b)//0xc00008e060

fmt.Println("b内存中存储的地址中的值", *b)//100

此时a和b内存中存储的地址是指向的同一个内存空间,所以修改了*b a 和 *b都会改变(可以看做这时a == *b)

*b=2000

fmt.Println("a",a)

fmt.Println("*b", *b)

指针如何使用

变量定义

varaint=100

指针变量的定义 正规的定义方式

varb*int= &a

指针变量的定义 偷懒的方式

c:= &a

fmt.Println("a的值",a)//100

fmt.Println("a的内存地址", &a)//0xc00000a0e8

fmt.Println("b变量中存的地址",b)//0xc00000a0e8

fmt.Println("b变量自己的内存地址", &b)//0xc000054068

fmt.Println("b变量中存的地址中的值", *b)//100

fmt.Println("c变量中存的地址",c)//0xc00000a0e8

fmt.Println("c变量自己的内存地址", &c)//0xc000054070

fmt.Println("c变量中存的地址中的值", *c)//100

通过指针b或指针c都可以改变a的值

*c=123

fmt.Println(a)

多级指针的使用(套娃)

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

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

例如:*ptr3 的类型就是 **int **ptr3 的类型就是*int ***ptr的类型就是int

varaint=100

varptr1*int= &a

varptr2**int= &ptr1

varptr3***int= &ptr2

fmt.Println("a的内存地址", &a)//0xc00000a0e8

fmt.Println("-----------------------------------------------")

fmt.Println("ptr1变量中存的内存地址",ptr1)//0xc00000a0e8

fmt.Println("ptr1变量自己的内存地址", &ptr1)//0xc000054068

fmt.Println("-----------------------------------------------")

fmt.Println("ptr2变量中存的内存地址",ptr2)//0xc000054068

fmt.Println("ptr2变量中存的内存地址中存的内存地址", *ptr2)//0xc00000a0e8

fmt.Println("ptr2变量自己的内存地址", &ptr2)//0xc000054070

fmt.Println("-----------------------------------------------")

fmt.Println("ptr3变量中存的内存地址",ptr3)//0xc000054070

fmt.Println("ptr3变量中存的内存地址中存的内存地址", *ptr3)//0xc000054068

fmt.Println("ptr3变量中存的内存地址中存的内存地址中存的内存地址", **ptr3)//0xc00000a0e8

fmt.Println("ptr3变量自己的内存地址", &ptr3)//0xc000054078

fmt.Printf("%T\n", *ptr3)

fmt.Printf("%T\n", **ptr3)

fmt.Printf("%T\n", ***ptr3)

//这时候改变a值的方式就很多了 因为*ptr1 **ptr2 ***ptr3 和 a 都是等价的,因为操控的是同一片内存空间

*ptr1=2000

fmt.Println(a)

**ptr2=3000

fmt.Println(a)

***ptr3=4000

fmt.Println(a)

数组指针

数组指针首先它是一个指针,然后指向了一个数组

定义一个数组

vararr= [4]int{1,2,3,4}

fmt.Println(arr)

fmt.Printf("数组的地址:%p\n", &arr)//0xc0000121c0

定义一个数组指针 指向数组

varptr_arr*[4]int= &arr

fmt.Println(ptr_arr)//&[1 2 3 4]

fmt.Println("取出内容:", *ptr_arr)//[1 2 3 4] 这时*ptr_arr == arr

fmt.Printf("ptr_arr变量中存的地址:%p\n",ptr_arr)//0xc0000121c0

fmt.Printf("ptr_arr变量自己的地址%p\n", &ptr_arr)//0xc000054070

通过指针操作数组 原生的操作方法

(*ptr_arr)[0] =100

fmt.Println(arr)//[100 2 3 4]

作为21世纪的新兴语言 不允许有这么的丑的代码出现

由于ptr_arr指向了arr 所以就出现了语法糖 可以像正常操作数组一样操作数组指针

ptr_arr[0] =123

fmt.Println(arr)

但这里需要注意的是,虽然因为语法糖的关系,可以用相同的方法操作。

但是它们的类型还是有本质区别的,一个是数组类型[4]int,一个是数组指针类型*[4]int

fmt.Printf("arr的类型%T\n",arr)//[4]int

fmt.Printf("ptr_arr的类型%T\n",ptr_arr)//*[4]int

指针数组

指针数组首先它是一个数组,数组里的每个元素保存的都是指针

a:=100

b:=200

c:=300

d:=400

创建一个指针数组,数组中存放的都是变量的地址

注意数组指针跟指针数组的区别: 数组指针*[4]int 指针数组[4]*int

ptr_arr:= [4]*int{&a, &b, &c, &d}

fmt.Println(ptr_arr)//[0xc00008c0a8 0xc00008c0c0 0xc00008c0c8 0xc00008c0d0]

通过指针修改变量的值

fmt.Println(a,b,c,d)//100 200 300 400

*ptr_arr[0] =666因为数组中的每个元素都是指针,所以要取出每个值都需要在取出元素的时候在前面再加上*号

fmt.Println(a,b,c,d)//666 200 300 400

指针函数

首先需要是一个函数,这个函数的返回值是一个指针

funcmain() {

调用了这个函数,可以得到一个指针类型的返回值

ptr:=fn()

fmt.Printf("ptr的类型:%T\n",ptr)//*[4]int

fmt.Printf("ptr的地址:%p\n", &ptr)//0xc00008e060

fmt.Println("ptr中的值:", *ptr)//[1 2 3 4]

回顾数组指针的操作方式(语法糖),我们就可以来愉快的操作这个数组了

fmt.Println((*ptr)[0])//原始的操作方式

ptr[0] =666//香香的语法糖

fmt.Println(ptr[0])

}

这个函数的返回值是一个指针

funcfn() *[4]int{

arr:= [4]int{1,2,3,4}

return&arr

}

指针作为函数参数

funcmain() {

a:=100

fmt.Printf("a变量的地址:%p\n", &a)//0xc00000a0e8

fmt.Printf("进入函数前 a的值 =%d\n",a)

updata1(a)

这里可以看出 因为值类型是拷贝的关系,并不会因为函数体内改变了a的值而改变a本身的值

fmt.Printf("函数结束后 a的值 =%d\n",a)

fmt.Printf("进入函数前 a的值 =%d\n",a)

updata2(&a)

这里可以看出 因为指针是引用类型传入的是地址,所以函数体内改变了a的值导致a本身的值也被改变了

fmt.Printf("函数结束后 a的值 =%d\n",a)

}

funcupdata1(aint) {

fmt.Println("--->",a)

fmt.Printf("a变量中的地址:%p\n", &a)//0xc00000a110

a=666

fmt.Println("--->",a)

}

funcupdata2(ptr*int) {

fmt.Println("--->", *ptr)

fmt.Printf("ptr变量中的地址:%p\n",ptr)//0xc00000a0e8

*ptr=666

fmt.Println("--->", *ptr)

}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:12:31

告别SQL方言困扰:sqlglot如何重塑你的数据开发工作流

告别SQL方言困扰:sqlglot如何重塑你的数据开发工作流 【免费下载链接】sqlglot tobymao/sqlglot: 这是一个用于SQL查询的构建器和解析器,支持多种数据库。适合用于需要动态构建和解析SQL查询的场景。特点:易于使用,支持多种数据库…

作者头像 李华
网站建设 2026/4/22 13:39:15

华秋DFM进行PCB对比、BOM分析、BOM对比

BOM分析、纠错 画完PCB会使用软件导出BOM,然后后续会在BOM上修改,添加各种信息等。等下单的时候担心BOM被后续误修改了跟原始PCB对不上。 这个时候就可以使用华秋DFM来检查,导入PCB和BOM,华秋DFM会自动提示PCB上的元件和BOM上对不…

作者头像 李华
网站建设 2026/4/23 11:12:34

企业级AI视频解决方案:基于Wan2.2-T2V-A14B构建私有化平台

企业级AI视频解决方案:基于Wan2.2-T2V-A14B构建私有化平台 你有没有想过,一条广告片的制作周期从几周缩短到几分钟?不是剪辑提速,而是“写出来就是视频”——这正是当下最前沿的企业级AI视频技术正在实现的事。 在数字营销、影视…

作者头像 李华
网站建设 2026/4/23 11:12:35

Agent性能飙升!Function Calling四大神级优化方案,让你的AI快如闪电!

在Agent智能体技术大爆发的今天,我们会经常听到这样的一个专业名词:Function calling。 但凡谈到智能体开发,就免不了需要让大模型通过Function calling功能来调用外部工具,我们评价模型的Agent能力,往往就是看模型的…

作者头像 李华