Golang不可寻址类型有哪些?详解不可寻址值
亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《Golang不可寻址类型有哪些?详解不可寻址值概念》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下,希望所有认真读完的童鞋们,都有实质性的提高。
在Go语言中,并非所有值都支持取地址,不可寻址的值主要包括:1.字面量和常量,它们不占用运行时内存地址;2.函数调用结果,因其为临时值;3.Map元素,因扩容可能导致地址失效;4.字符串的字节或字符,因字符串不可变;5.某些表达式的中间结果,如算术运算结果;设计上限制不可寻址是为了保障数据安全、并发安全及编译优化;应对方式包括将不可寻址值赋给变量后再取地址,或在map中存储指针类型以实现修改需求。
Golang中,并非所有类型都“不支持取地址”,更准确的说法是,某些“值”或“表达式的结果”是不可寻址的。这通常发生在它们不是一个可修改的变量,或者它们的内存地址不稳定时。你可以理解为,你只能获取一个稳定、有归属的“家”的地址,而那些临时的、瞬时的、或者根本就没有固定“家”的东西,自然就无法被“寻址”了。

解决方案
在Go语言里,当你尝试使用 &
运算符去获取一个值的内存地址时,如果这个值是“不可寻址的”(non-addressable),编译器就会报错。那么,哪些情况会产生不可寻址的值呢?
字面量(Literals)和常量(Constants): 无论是数字、字符串、布尔值,还是
nil
,它们都是字面量。常量在编译时通常会被内联,不占用独立的运行时内存地址。// 无效:字面量不可寻址 // p := &10 // s := &"hello" // b := &true const MyConst = 100 // 无效:常量不可寻址 // pConst := &MyConst
这很好理解,你不能改变数字
10
本身代表的意义,它只是一个值。函数调用结果(Function Call Results): 当一个函数返回一个值时(而不是一个指针),这个返回值是一个临时拷贝,它没有一个稳定的内存地址。
func getValue() int { return 42 } // 无效:函数返回值是临时值 // p := &getValue()
这个
42
就像是快递员暂时放在你手里的包裹,你不能直接给这个包裹贴个永久的地址标签,你得先把它放进你家里(一个变量),才能给你的家贴地址。Map的元素(Map Elements): 这是Go语言设计中一个重要的安全考量。当你通过
m[key]
访问一个map元素时,你得到的是该元素的一个副本。更重要的是,Go的map在内部实现时,可能会因为扩容等操作而移动其存储的元素。如果允许直接取地址,那么这个地址可能会在map内部重新分配时变得无效,导致悬空指针(dangling pointer)问题,这会非常危险,尤其是在并发场景下。m := make(map[string]int) m["age"] = 30 // 无效:map元素不可寻址 // pAge := &m["age"]
如果你需要修改map中某个复杂类型的值的内部字段,通常的做法是先取出值,修改后,再重新赋值回去,或者直接在map中存储指针。
字符串的字节或字符(Bytes or Characters of a String): 字符串在Go中是不可变的(immutable)。这意味着你不能通过指针去修改字符串的某个字节。当你访问
s[i]
时,你得到的是一个字节的副本。s := "hello" // 无效:字符串元素不可寻址 // pByte := &s[0]
这和字符串的不可变性是紧密相关的,如果你能拿到某个字符的地址并修改它,那就违背了字符串不可变的原则。
某些表达式的中间结果: 比如算术运算的结果
(a + b)
,它们是临时的计算结果,同样不具备稳定的内存地址。a, b := 1, 2 // 无效:表达式结果不可寻址 // pSum := &(a + b)
为什么Go语言要限制某些值不可寻址?——深入理解设计哲学
我觉得Go语言在“可寻址性”上的设计,是其“简单”和“安全”哲学的一个缩影。它并非随意设限,而是出于多方面的考量:
首先是数据完整性和安全性。想象一下,如果可以获取并修改一个字面量 10
的地址,那么 *p = 20
之后,所有引用 10
的地方是不是都变成了 20
?这显然是荒谬且不可控的。对于常量,它们在编译阶段就已确定,甚至可能直接被编译器内联到使用它们的地方,根本没有运行时地址的概念。限制这些值的寻址,保证了它们的不变性,从而避免了许多潜在的逻辑错误和难以追踪的bug。
其次是并发安全,特别是针对map。Go语言的map不是并发安全的,如果你在多个goroutine中同时读写一个map,需要外部加锁。而如果 m[key]
这样的表达式可以直接取地址,那么程序员可能会误以为可以通过这个指针安全地修改map内部的值。然而,map在扩容或重新哈希时,其内部元素的内存位置可能会发生变化,导致之前获取的指针变成“野指针”或“悬空指针”。Go通过强制要求你不能直接取map元素的地址,间接推动你采用更安全的并发模式(例如,先取出值,修改后再存回,或者直接在map中存储指向值的指针)。这是一种“防患于未然”的设计,它在编译期就阻止了潜在的运行时陷阱。
再者,这与编译器优化也有关系。那些临时值、表达式结果,如果允许取地址,编译器就不得不为它们分配实际的内存空间,这可能会限制一些优化手段,比如寄存器分配、值传递等。通过限制,编译器可以更自由地处理这些瞬时值,提高程序的运行效率。从我的经验来看,Go的编译器在逃逸分析方面做得很好,它会尽量将变量分配在栈上,避免不必要的堆分配。而不可寻址值的概念,正是这种优化策略的一部分。
最后,它也带来了一种清晰的编程模型。当你看到一个值不能取地址时,你就知道它不是一个“变量”或者“可修改的内存位置”,它更多的是一个“数据快照”或者“临时结果”。这种清晰的区分有助于开发者更好地理解数据的生命周期和可变性,减少概念上的混淆。
如何处理不可寻址值的常见场景及实践?——实用技巧与最佳实践
面对不可寻址值的限制,Go语言提供了非常直接且符合逻辑的处理方式,通常只需要稍作变通。
最常见也是最直接的方法,就是将其赋值给一个可寻址的变量。如果你确实需要一个指针指向某个值,那么这个值必须先“安家落户”在一个变量里。
// 场景一:需要一个指向字面量或常量值的指针 // 无效:p := &10 num := 10 // 将字面量赋值给一个变量 p := &num // 现在可以取地址了 const MaxValue = 100 // 无效:ptr := &MaxValue val := MaxValue // 将常量赋值给一个变量 ptr := &val // 现在可以取地址了
这种模式非常普遍,尤其是在需要将基本类型值传入接受指针参数的函数时(例如,某些库函数可能需要 *int
而不是 int
)。
处理map元素: 如果你需要在map中存储复杂类型(如结构体),并且希望能够通过指针来修改这些结构体的内部字段,那么最佳实践是在map中存储指向结构体的指针,而不是结构体本身。
type User struct { ID int Name string } users := make(map[int]*User) // map的值类型是 *User // 存储一个User的指针 user1 := &User{ID: 1, Name: "Alice"} users[1] = user1 // 现在可以直接通过map访问并修改User的字段,因为map中存储的是指针 users[1].Name = "Alicia" // 有效! fmt.Println(users[1].Name) // 输出:Alicia // 如果map存储的是值类型,你必须取出,修改,再放回 // usersValue := make(map[int]User) // usersValue[2] = User{ID: 2, Name: "Bob"} // tempUser := usersValue[2] // 取出副本 // tempUser.Name = "Robert" // 修改副本 // usersValue[2] = tempUser // 重新赋值回map
这种模式清晰地表达了你的意图:你希望能够直接操作map中存储的“对象”,而不是其副本。
**结构体字
本篇关于《Golang不可寻址类型有哪些?详解不可寻址值》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!

- 上一篇
- Golang文件IO错误处理详解

- 下一篇
- JavaWebSocket消息重发方案详解
-
- Golang · Go教程 | 1小时前 |
- Golangzip库实现文件压缩解压教程
- 244浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang外观模式:简化接口的实用技巧
- 339浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golangio/fs文件系统解析与内存实现详解
- 347浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang组合模式应用与树形操作实现
- 295浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang反射解析协议,二进制转结构体教程
- 344浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang模拟网络延迟测试方法
- 357浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang集成Istio与Envoy配置详解
- 313浏览 收藏
-
- Golang · Go教程 | 2小时前 | 数据竞争 通道 同步原语 Golang并发内存模型 happens-before原则
- Golang并发内存模型解析:happens-before原则详解
- 318浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang快速搭建HTTP服务器方法
- 427浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- GolangCSV文件读写教程详解
- 365浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang结构体定义及使用教程
- 265浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- TextIn智能文字识别平台
- TextIn智能文字识别平台,提供OCR、文档解析及NLP技术,实现文档采集、分类、信息抽取及智能审核全流程自动化。降低90%人工审核成本,提升企业效率。
- 8次使用
-
- 简篇AI排版
- SEO 简篇 AI 排版,一款强大的 AI 图文排版工具,3 秒生成专业文章。智能排版、AI 对话优化,支持工作汇报、家校通知等数百场景。会员畅享海量素材、专属客服,多格式导出,一键分享。
- 8次使用
-
- 小墨鹰AI快排
- SEO 小墨鹰 AI 快排,新媒体运营必备!30 秒自动完成公众号图文排版,更有 AI 写作助手、图片去水印等功能。海量素材模板,一键秒刷,提升运营效率!
- 9次使用
-
- Aifooler
- AI Fooler是一款免费在线AI音频处理工具,无需注册安装,即可快速实现人声分离、伴奏提取。适用于音乐编辑、视频制作、练唱素材等场景,提升音频创作效率。
- 9次使用
-
- 易我人声分离
- 告别传统音频处理的繁琐!易我人声分离,基于深度学习的AI工具,轻松分离人声和背景音乐,支持在线使用,无需安装,简单三步,高效便捷。
- 9次使用
-
- Golangmap实践及实现原理解析
- 2022-12-28 505浏览
-
- 试了下Golang实现try catch的方法
- 2022-12-27 502浏览
-
- Go语言中Slice常见陷阱与避免方法详解
- 2023-02-25 501浏览
-
- Golang中for循环遍历避坑指南
- 2023-05-12 501浏览
-
- Go语言中的RPC框架原理与应用
- 2023-06-01 501浏览