> ## Content Index
> Fetch the complete content index at: https://huizhou92.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Go高性能编程 EP1 : empty struct
- URL: https://huizhou92.com/gogao-xing-neng-bian-cheng-ep1-empty-struct/
- Published: 2024-06-17T15:18:02.000Z
- Updated: 2026-09-06T11:39:57.000Z
- Description: 探索 Go 语言中的空结构体及其独特特性。了解为何空结构体的大小为 0 以及如何在内存消耗和性能优化中发挥作用。通过示例解释空结构体在 map 和 chan 中的使用，揭示其在编程中的实际应用场景和价值。
- Author: huizhou92
- Tags: tech, golang, #lang-zh-cn

在 go语言中，正常的 struct 一定是需要占用一块内存的，但是有一种特殊情况，如果是一个空struct，那么它的大小为0\. 这是怎么回事，空struct 又有什么用呢？

```go
  
type Test struct {  
    A int  
    B string  
}    
func main() {  
    fmt.Println(unsafe.Sizeof(new(Test)))  
    fmt.Println(unsafe.Sizeof(struct{}{}))  
}  
/*  
8  
0  
*/

```

## Empty Struct 的 秘密

### 特殊变量：zerobase

空结构体是没有内存大小的结构体。这句话是没有错的，但是更准确的来说，其实是有一个特殊起点的，那就是 `zerobase` 变量，这是一个 `uintptr` 全局变量，占用 8 个字节。当在任何地方定义无数个 `struct {}` 类型的变量，编译器都只是把这个 `zerobase` 变量的地址给出去。换句话说，在 golang 里面，涉及到所有内存 size 为 0 的内存分配，那么就是用的同一个地址 `&zerobase` 。  
[例如](https://go.dev/play/p/WNxfXviET%5Fi?ref=huizhou92.com)

```go
// https://go.dev/play/p/WNxfXviET_i
package main

import "fmt"

type emptyStruct struct {}

func main() {
	a := struct{}{}
	b := struct{}{}
	c := emptyStruct{}

	fmt.Printf("%p\n", &a)
	fmt.Printf("%p\n", &b)
	fmt.Printf("%p\n", &c)
}

// 0x58e360
// 0x58e360
// 0x58e360

```

空结构体的变量的内存地址都是一样的。这是因为编译器在编译期间，遇到 `struct {}` 这种特殊类型的内存分配，会给他分配`&zerobase`，这个代码逻辑是在 [mallocgc](https://go.googlesource.com/go/blob/fe36ce669c1a452d2b0e81108a7e07674b50692a/src/runtime/malloc.go?ref=huizhou92.com#L983) 函数里面：

```go
//go:linkname mallocgc  
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {  
    ....
    if size == 0 {  
       return unsafe.Pointer(&zerobase)  
    }
    .....

```

这就是`Empty struct` 的秘密有了这个特殊的 变量，我们利用它可以完成很多功能。

Empty struct 与内存对其  
一般情况下，struct 中包含 empty struct ，这个字段是不占用内存空间的，但是有一种情况是特殊的，那就是 empty struct 位于最后一位，它会触发内存对齐 。  
[比如](https://go.dev/play/p/HcxlywljovS?ref=huizhou92.com)

```go
//https://go.dev/play/p/HcxlywljovS
type A struct {
	x int
	y string
	z struct{}
}
type B struct {
	x int
	z struct{}
	y string
}

func main() {
	println(unsafe.Alignof(A{}))
	println(unsafe.Alignof(B{}))
	println(unsafe.Sizeof(A{}))
	println(unsafe.Sizeof(B{}))
}

/**
8
8
32
24
**/

```

因为如果有指针指向该字段, 返回的地址将在结构体之外，如果此指针一直存活不释放对应的内存，就会有内存泄露的问题（该内存不因结构体释放而释放）。  
因此，当 `struct{}` 作为其他 struct 最后一个字段时，需要填充额外的内存保证安全,如果 empty struct 在开始位置，或者中间位置，那么它的地址是下一个变量的地址

```go
type A struct {  
    x int  
    y string  
    z struct{}  
}  
type B struct {  
    x int  
    z struct{}  
    y string  
}  
  
func main() {  
    a := A{}  
    b := B{}  
    fmt.Printf("%p\n", &a.y)  
    fmt.Printf("%p\n", &a.z)  
    fmt.Printf("%p\n", &b.y)  
    fmt.Printf("%p\n", &b.z)  
}
/**
0x1400012c008
0x1400012c018
0x1400012e008
0x1400012e008
**/

```

## Empty 的使用场景

空结构体 `struct{ }` 为什么会存在的核心理由就是为了**节省内存**。当你需要一个结构体，但是却丝毫不关系里面的内容，那么就可以考虑空结构体。golang 核心的几个复合结构 `map` ，`chan` ，`slice` 都能结合 `struct{}` 使用。

### `map` & `struct{}`

```go
// 创建 map
m := make(map[int]struct{})
// 赋值
m[1] = struct{}{}
// 判断 key 键存不存在
_, ok := m[1]

```

### `chan` & `struct{}`

`channel` 和 `struct{}` 结合是一个最经典的场景，`struct{}` 通常作为一个信号来传输，并不关注其中内容。chan 的分析在前几篇文章有详细说明。chan 本质的数据结构是一个管理结构加上一个 ringbuffer ，如果 `struct{}` 作为元素的话，ringbuffer 就是 0 分配的。

`chan` 和 `struct{}` 结合基本只有一种用法，就是**信号传递**，空结构体本身携带不了值，所以也只有这一种用法啦，一般来说，配合 no buffer 的 channel 使用。

```go
// 创建一个信号通道
waitc := make(chan struct{})

// ...
goroutine 1:
    // 发送信号: 投递元素
    waitc <- struct{}
    // 发送信号: 关闭
    close(waitc)

goroutine 2:
    select {
    // 收到信号，做出对应的动作
    case <-waitc:
    }    

```

这种场景我们思考下，是否一定是非 `struct{}` 不可？其实不是，而且也不多这几个字节的内存，所以这种情况真的就只是不关心 `chan` 的元素值而已，所以才用的 `struct{}`。

## 总结

1. 空结构体也是结构体，只是 size 为 0 的类型而已；
2. 所有的空结构体都有一个共同的地址：`zerobase` 的地址；
3. 我们可以利用empty struct 不占用内存的特性，来优化代码，比如利用map 实现set 以及 chan 等。

## 参考链接

1. [The empty struct, Dave Cheney](https://dave.cheney.net/2014/03/25/the-empty-struct?ref=huizhou92.com)
2. [Go 最细节篇— struct{} 空结构体究竟是啥？](https://www.qiyacloud.cn/2020/12/2020-12-21/?ref=huizhou92.com)