广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Go语言空结构体详解
  • 872
分享到

Go语言空结构体详解

2024-04-02 19:04:59 872人浏览 泡泡鱼
摘要

目录前言什么是空结构体特点地址相同大小为0原理探究使用场景集合(Set)channel中信号传输总结前言 在使用 Go 语言开发过程中,我们不免会定义结构体,但是我们定义的结构体都是

前言

在使用 Go 语言开发过程中,我们不免会定义结构体,但是我们定义的结构体都是有字段的,基本不会定义不包含字段的 空结构体。你可能会反过来问,没有字段的空结构体有什么用呢?那么我们这篇文章就来研究下空结构体吧!

注:本文基于go 1.14.4 分析

什么是空结构体

我们说不包含任何字段的结构体叫做空结构体,可以通过如下的方式定义空结构体:

原生定义

var a struct{}

类型别名

type empty struct{}
var e empty

特点

地址相同

我们分别定义两个非空结构体和空结构体变量,然后取地址打印,发现空结构体变量的地址是相同的:

// 定义一个非空结构体
type User struct {
    name string
}

func main() {
 
  // 两个非空结构体的变量地址不同
  var user1 User
    var user2 User
    fmt.Printf("%p \n", &user1) // 0xc000318670
    fmt.Printf("%p \n", &user2) // 0xc000318680
  
  // 定义两个空结构体,地址相同
    var first struct{}
    var second struct{}
    fmt.Printf("%p \n", &first)    // 0x1ca15f0 
    fmt.Printf("%p \n", &second)   // 0x1ca15f0 
}

我们知道 Go 语言中的变量传递都是值传递,对于传参前后的变量地址应该不同,我们通过传参的方式再来试一下:

// 非空结构体
type NonEmptyUser struct {
    name string
}

// 空结构体
type EmptyUser struct{}

// 打印非空结构体参数地址
func testNonEmptyUser(user NonEmptyUser) {
    fmt.Printf("%p \n", &user)
}

// 打印空结构体参数地址
func testEmptyUser(user EmptyUser) {
    fmt.Printf("%p \n", &user)
}


func main() {
  
    // 两个非空结构体的变量地址不同
    var user1 NonEmptyUser
    fmt.Printf("%p \n", &user1) // 0xc0001986c0
    testNonEmptyUser(user1)            // 0xc0001986d0

  
    // 两个空结构体变量的地址相同
    var user2 EmptyUser
    fmt.Printf("%p \n", &user2) // 0x1ca25f0
    testEmptyUser(user2)                // 0x1ca25f0
  
}

发现对于非空结构体,传参前后的地址是不同的,但是对于空结构体变量,前后地址是一致的。

大小为0

在Go中,我们可以使用 unsafe.Sizeof 来计算一个变量占用的字节数,那么就举几个例子来看下:

type EmptyUser struct{}

func main() {
    var i int
    var s string
    var m []string
    var u EmptyUser
  
    fmt.Println(unsafe.Sizeof(i)) // 8
    fmt.Println(unsafe.Sizeof(s)) // 16
    fmt.Println(unsafe.Sizeof(m)) // 24
    fmt.Println(unsafe.Sizeof(u)) // 0
}

可以看到空结构体占用的内存空间大小为0,同时对于空结构体的组合,占用空间大小也为0:

// 空结构体的组合
type EmptyUser struct {
    name struct{}
    age  struct{}
}

func main() {
    
    var u EmptyUser

    fmt.Println(unsafe.Sizeof(u)) // 0
}

原理探究

为什么空结构体的地址都相同,而且大小都为0呢,我们一起来看下源码(go/src/runtime/malloc.go):

// base address for all 0-byte allocations
var zerobase uintptr

// 创建新的对象时,调用 mallocGC 分配内存
func newobject(typ *_type) unsafe.Pointer {
    return mallocgc(typ.size, typ, true)
}

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    if gcphase == _GCmarktermination {
        throw("mallocgc called with gcphase == _GCmarktermination")
    }

    if size == 0 {
        return unsafe.Pointer(&zerobase)
    }
    ......
}

通过源码可以看出,创建新的对象时,需要调用 malloc.newobject() 进行内存分配,进一步调用 mallocgc 方法,在该方法中,如果判断类型的 size==0 ,固定返回 zerobase 的地址。 zerobase 是一个 uintptr 全局变量,占用 8 个字节。

因此我们可以确定的是,在Go语言中,所有针对 size==0 的内存分配,用的都是同一个地址 &zerobase ,所以我们在一开始看到的所有空结构体地址都相同。

使用场景

空结构体不包含任何数据,那么其应用场景也应该不在乎值内容,只当做一个占位符。在这种场景下,由于其不占用内存空间,使用空结构体既可以做到节省空间,又可以提供语义支持。

集合(Set)

使用过 Java 的同学应该都用过 Set 类型,Set 是保存不重复元素的集合,但是 Go 语言没有提供原生的 Set 类型。但是我们知道 Map 结构存储的是 key-value 类型,key 不允许重复,因此可以利用 Map 来实现 Set,key存储需要的数据,value 给个固定值就可以了。那么 value 给什么值好呢?这时候我们的 空结构体 就可以出场了,不占用空间,还可以完成占位操作,堪称完美,下面我们看怎么实现吧。

// 定义了一个保存 string 类型的 Set集合
type Set map[string]struct{}

// 添加一个元素
func (s Set) Add(key string) {
    s[key] = struct{}{}
}

// 移除一个元素
func (s Set) Remove(key string) {
    delete(s, key)
}

// 是否包含一个元素
func (s Set) Contains(key string) bool {
    _, ok := s[key]
    return ok
}

// 初始化
func NewSet() Set {
    s := make(Set)
    return s
}
// 测试使用
func main() {
    set := NewSet()
    set.Add("hello")
    set.Add("world")
    fmt.Println(set.Contains("hello"))

    set.Remove("hello")
    fmt.Println(set.Contains("hello"))
}

channel中信号传输

空结构体 与 channel 可谓是一个经典组合,有时候我们只是需要一个信号来控制程序的运行逻辑,并不在意其内容如何。

在下面的例子中,我们定义了两个 channel 用于接收两个任务完成的信号,当接收到任务完成的信号时,就会触发相应的动作。

func doTask1(ch chan struct{}) {
    time.Sleep(time.Second)
    fmt.Println("do task1")
    ch <- struct{}{}
}

func doTask2(ch chan struct{}) {
    time.Sleep(time.Second * 2)
    fmt.Println("do task2")
    ch <- struct{}{}
}

func main() {

    ch1 := make(chan struct{})
    ch2 := make(chan struct{})
    go doTask1(ch1)
    go doTask2(ch2)

    for {
        select {
        case <-ch1:
            fmt.Println("task1 done")
        case <-ch2:
            fmt.Println("task2 done")
        case <-time.After(time.Second * 5):
            fmt.Println("after 5 seconds")
            return
        }
    }
}

总结

本篇文章,我们学习了如下内容:

  • 空结构体是一种特殊的结构体,不包含任何元素
  • 空结构体的大小都为0
  • 空结构体的地址都相同
  • 由于空结构体不占用空间,从节省内存的角度出发,适用于实现Set结构、在 channel 中传输信号等

 到此这篇关于Go语言空结构体详解的文章就介绍到这了,更多相关Go语言空结构体内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Go语言空结构体详解

本文链接: https://www.lsjlt.com/news/163408.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Go语言空结构体详解
    目录前言什么是空结构体特点地址相同大小为0原理探究使用场景集合(Set)channel中信号传输总结前言 在使用 Go 语言开发过程中,我们不免会定义结构体,但是我们定义的结构体都是...
    99+
    2022-11-13
  • Go语言结构体struct详解,Go空结构体的这些妙用你知道吗?
    本文详解了Go语言结构体的各个知识点,最后介绍了空结构体的3种妙用。希望对你有帮助。 定义 结构体,是一种自定义的数据类型,由多个数据类型组合而成。用于描述一类事物相关属性。 定义方式: typ...
    99+
    2023-09-07
    golang java 算法
  • Go语言空结构体是什么
    这篇文章主要为大家展示了“Go语言空结构体是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Go语言空结构体是什么”这篇文章吧。什么是空结构体我们说不包含任何字段的结构体叫做空结构体,可以通过...
    99+
    2023-06-28
  • go语言结构体指针操作示例详解
    目录指针go指针操作不能操作不合法指向new函数指针做函数的参数数组指针结构体指针变量结构体成员普通变量结构体成员指针变量结构体比较和赋值结构体作为函数参数指针 指针是代表某个内存地...
    99+
    2022-11-13
  • C语言结构体struct详解
    目录结构体的概念结构体类型的声明结构体变量的创建typedef关键字结构体的嵌套结构体变量的初始化结构体成员的访问结构体的传参总结结构体的概念 结构体是由一系列具有相同类型或不同类型...
    99+
    2022-11-13
  • Go语言基础语法之结构体及方法详解
    结构体类型可以用来保存不同类型的数据,也可以通过方法的形式来声明它的行为。本文将介绍go语言中的结构体和方法,以及“继承”的实现方法。 结构体类型 结构体类型(struct)在go语...
    99+
    2022-11-12
  • Go语言基础结构体用法及示例详解
    目录概述语法结构体定义的三种形式第一种【基本的实例化】第二种【指针类型的结构体】第三种【取结构体的地址实例化,通过&的操作】初始化结构体键值对初始化结构体值列表填充结构体匿名...
    99+
    2022-11-12
  • 详解Go语言中结构体与JSON间的转换
    目录前言结构体转 JSONJSON 解析结构体小结前言 在日常开发中,我们往往会将 JSON 解析成对应的结构体,反之也会将结构体转成 JSON。接下来本文会通过 JSON 包的两个...
    99+
    2022-12-14
    Go语言 结构体 JSON转换 Go语言 结构体 JSON Go语言结构体 Go语言 JSON
  • C语言之结构体(struct)详解
    目录为什么需要引入结构体struct定义typedef与#define结构体变量初始化及成员访问结构体访问总结为什么需要引入结构体 原有的数据类型不能满足需求,因此才设计了构造类型结...
    99+
    2022-11-12
  • Go语言中的Struct结构体
    一、Struct结构体 Go语言中没有像C#、Java一样的Class,只有Struct这样的结构体。Go语言使用type关键字来定义一个类型。 如下: type User stru...
    99+
    2022-11-13
  • 如何在go语言项目中使用空结构体
    本篇文章给大家分享的是有关如何在go语言项目中使用空结构体,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。用法:1、将map作为Set使用时,可以将值类型定义为空结构体,仅作为占...
    99+
    2023-06-15
  • 细说Go语言中空结构体的奇妙用途
    目录1. 空结构体的定义和初始化2. 空结构体的大小和内存占用3. 空结构体作为占位符4. 空结构体作为通道元素5. 空结构体作为 map 的占位符6. 空结构体作为方法接收器7. ...
    99+
    2023-05-18
    Go语言空结构体使用 Go语言空结构体 Go语言 结构体
  • Go语言学习教程之结构体的示例详解
    目录前言可导出的标识符嵌入字段提升标签结构体与JSON相互转换结构体转JSONJSON转结构体练习代码步骤前言 结构体是一个序列,包含一些被命名的元素,这些被命名的元素称为字段(fi...
    99+
    2022-11-11
  • Go语言学习之结构体和方法使用详解
    目录1. 结构体别名定义2. 工厂模式3. Tag 原信息4. 匿名字段5. 方法1. 结构体别名定义 变量别名定义 package main import "fmt" type...
    99+
    2022-11-13
  • C语言结构体超详细讲解
    目录前言1、结构体的声明1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的定义和初始化2、结构体成员的访问2.1 点操作符访问2.2 ->操作符...
    99+
    2022-11-13
  • C语言结构体,枚举,联合体详解
    目录1.什么是结构体、枚举、联合体2.定义结构体2.1 包含结构体成员变量、variable2.2 tag、结构体成员变量2.3 用结构体声名变量2.4 用typedef 创建新类型...
    99+
    2022-11-12
  • Go语言之结构体与方法
    目录一、结构体1、结构体的定义与使用2、定义并赋初值3、匿名结构体(只使用一次,没有名字)4、结构体的零值5、结构体的指针6、匿名字段(字段没有名字,只有类型)7、嵌套结构体(结构体...
    99+
    2022-11-12
  • go语言结构体怎么赋值
    在Go语言中,结构体的赋值可以通过两种方式进行:1. 通过字段名逐个赋值:可以使用`结构体变量名.字段名`的方式进行赋值。例如:``...
    99+
    2023-10-10
    go语言
  • C语言 struct结构体超详细讲解
    目录一、本章重点二、创建结构体三、typedef与结构体的渊源四、匿名结构体五、结构体大小六、结构体指针七、其他一、本章重点 创建结构体typedef与结构体的渊源匿名结构体结构体大...
    99+
    2022-11-13
  • 详解C语言中结构体的使用
    目录结构体的声明结构体成员的类型结构体成员的访问结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量。 举例: //定义结构体类...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作