揭秘 Go 语言中空结构体的强大用法

简介: Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。

0.前言

在 Go 语言中,空结构体 struct{} 是一个非常特殊的类型。它不包含任何字段,并且不占用任何内存空间。虽然乍一看似乎没什么用,但实际上,空结构体在 Go 编程中有着广泛的应用。本文将结合工作实例详细探讨空结构体的几种典型用法,并解释为什么它们在特定场景下非常有用。


1.特性

1.1 不占用内存空间

接下来我们来验证空结构体是否占用内存空间,并查找不同空结构体的内存地址是否一致。

代码:

QQ_1740625909307.png

输出:

QQ_1740625987970.png

由以上结果可知:

  1. 多个空结构体内存地址相同。
  2. 空结构体占用字节数为 0,即不占用内存空间。
  3. 多个空结构体值相等。

注意:多个空结构体内存地址 可能 相同,具体与 Go 编译器实现有关。

1.2 内存地址可能相同

1.1中我们验证空结构体的内存大小时,也验证了他们的内存地址,发现它们的内存地址一致,那么是不是巧合呢?


代码:

image.png

输出:

QQ_1740626453577.png

查阅相关文章和部分源码后,原因可能如下:

Go 语言的运行时环境中有一个名为 zerobase 的全局变量,它是一个 uintptr 类型,占用 8 个字节。每当分配的内存大小为 0 时,Go 语言会返回这个 zerobase 的地址,而不是分配新的内存空间1。因此,当你创建多个空结构体时,它们可能会返回相同的 zerobase 地址。

1.3 空结构体占用内存空间不为空的特例

空结构体也并不是什么时候都不会占用内存空间,比如空结构体作为另一个结构体字段时,根据位置不同,可能因内存对齐原因,导致外层结构体大小不一。

代码:

image.png

输出:


image.png


可以发现,当空结构体放在另一个结构体最后一个字段时,会触发内存对齐。如果你的程序对内存要求比较严格,则在使用空结构体作为字段时需要考虑这一点。


2. 用法

2.1 实现Go中的set类型:map+struct

空结构体最常用的地方,就是用来实现 set(集合) 类型了。Go 语言在语法层面没有提供 set 类型,但我们可以很方便地使用 map + struct{} 来实现 set 类型,

代码:

image.png

输出:

true

false

false

2.2 信号通知

空结构体另一个经常使用的方法是与 channel 结合当作信号来使用,示例如下:


image.png

输出:

waiting...

goroutine done

main exit

由于 struct{} 并不占用内存,所以实际上 channel 内部只需要将计数器加一即可,不涉及数据传输,故没有额外内存开销。

2.3 申请超大容量Slice

代码:

QQ_1740627891957.png

输出:

slice a size: 24

slice b size: 24


2.4 申请超大容量Array

基于空结构体不占用内存空间的特性,试创建个容量为 100万 的array。

代码:

QQ_1740627714232.png

输出:

array a size: 16000000

array b size: 0

输出可能会因系统架构和 Go 版本而异。

2.5 作为接口实现

代码:

image.png

输出:

写入数据前:Hello, World

!写入了 13 个字节

数据已被丢弃。

3 总结

空结构体 struct{} 在 Go 中虽小却有着巧妙的用途。从节省内存的角度看,它是表示空概念的理想选择。从语义上考虑,使用 struct{} 语义更明确,就是不关注值。由于内存对齐的影响,空结构体字段顺序可能影响外层结构体的大小,建议将空结构体放在外层结构体的第一个字段。无论是使用空结构体实现集合、信号通知和接口等,struct{} 都显示了其独特的价值。











引用:重新认识Golang中的空结构体


相关文章
|
2月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
226 2
|
4月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
314 1
|
4月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
409 0
|
4月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
272 0
|
4月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
252 0
|
4月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
348 0
|
4月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
10月前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
4月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
6月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:路由、中间件、参数校验
Gin框架以其极简风格、强大路由管理、灵活中间件机制及参数绑定校验系统著称。本文详解其核心功能:1) 路由管理,支持分组与路径参数;2) 中间件机制,实现全局与局部控制;3) 参数绑定,涵盖多种来源;4) 结构体绑定与字段校验,确保数据合法性;5) 自定义校验器扩展功能;6) 统一错误处理提升用户体验。Gin以清晰模块化、流程可控及自动化校验等优势,成为开发者的优选工具。

热门文章

最新文章