0

0

Go语言中uint64的存储:固定内存分配与变长编码解析

DDD

DDD

发布时间:2025-10-17 10:12:47

|

381人浏览过

|

来源于php中文网

原创

Go语言中uint64的存储:固定内存分配与变长编码解析

go语言中,`uint64`类型变量在内存中始终占用8字节的固定空间。然而,当使用`binary.putuvarint`等函数进行序列化时,`uint64`值可能被编码为多达10字节的变长数据。这种差异源于go的varint编码设计,它优先考虑编码格式的通用性和一致性,而非在特定情况下最小化64位值的字节数。

在Go语言的开发实践中,理解基本数据类型在内存中的存储方式以及它们在不同场景下的编码表现至关重要。特别是对于uint64这种大整数类型,其内存占用与序列化编码之间存在着值得深入探讨的差异。

Go语言中uint64的固定内存分配

Go语言规范明确定义了各种基本数据类型在内存中的固定大小。对于uint64类型,无论其存储的数值大小如何(从0到2^64-1),它在内存中始终占用8个字节。这是Go语言为了保证内存访问效率和可预测性而做出的设计选择。

根据Go语言官方文档Size and alignment guarantees的规定,常见类型的大小如下:

type                                 size in bytes

byte, uint8, int8                     1
uint16, int16                         2
uint32, int32, float32                4
uint64, int64, float64, complex64     8
complex128                           16

这意味着,当你声明一个uint64类型的变量时,系统会为其分配8字节的内存空间,这个空间大小是固定的,与变量实际存储的数值无关。例如,uint64(1)和uint64(math.MaxUint64)在内存中都占用8字节。

立即学习go语言免费学习笔记(深入)”;

我们可以通过unsafe.Sizeof函数来验证这一点:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var u64_small uint64 = 1
    var u64_large uint64 = ^uint64(0) // Max uint64 value (2^64 - 1)

    fmt.Printf("变量 u64_small (%d) 在内存中占用 %d 字节\n", u64_small, unsafe.Sizeof(u64_small))
    fmt.Printf("变量 u64_large (%d) 在内存中占用 %d 字节\n", u64_large, unsafe.Sizeof(u64_large))
}

运行上述代码,会输出:

变量 u64_small (1) 在内存中占用 8 字节
变量 u64_large (18446744073709551615) 在内存中占用 8 字节

这清晰地表明了uint64在内存中的固定大小特性。

binary.PutUvarint的变长编码(Varint)

尽管uint64在内存中是固定8字节,但在数据序列化(例如,网络传输、文件存储)的场景中,Go语言提供了变长编码(Varint)机制,以实现更高效的空间利用。encoding/binary包中的PutUvarint函数就是用于将uint64值编码为变长字节序列的。

Varint编码的原理是:对于较小的数值,使用较少的字节表示;对于较大的数值,则使用更多的字节。这种编码方式在数据分布集中于较小数值时,能显著节省存储空间。

艺映AI
艺映AI

艺映AI - 免费AI视频创作工具

下载

然而,一个有趣的现象是,binary.PutUvarint在编码一个uint64值时,最多可能占用10个字节,这超出了uint64本身的8字节内存大小。这并非设计错误,而是Go语言为了保持编码格式的通用性和一致性而做出的权衡。

Go标准库的encoding/binary包中的设计说明解释了这一决策:

// Design note:
// At most 10 bytes are needed for 64-bit values. The encoding could
// be more dense: a full 64-bit value needs an extra byte just to hold bit 63.
// Instead, the msb of the previous byte could be used to hold bit 63 since we
// know there can't be more than 64 bits. This is a trivial improvement and
// would reduce the maximum encoding length to 9 bytes. However, it breaks the
// invariant that the msb is always the "continuation bit" and thus makes the
// format incompatible with a varint encoding for larger numbers (say 128-bit).

这段说明揭示了关键点:

  1. 最大10字节:对于64位值,最多需要10个字节进行编码。
  2. “延续位”(Continuation Bit)的不变性:Go的varint编码方案中,每个字节的最高有效位(MSB)被用作“延续位”。如果MSB为1,表示当前数字还有后续字节;如果MSB为0,表示这是数字的最后一个字节。
  3. 设计权衡:为了保持这种“延续位”不变性,并使编码格式能够兼容未来可能出现的更大数字(如128位),Go选择了一种在某些情况下(尤其是当uint64的第63位被设置时)会使用额外一个字节的方案。如果为了将最大编码长度缩减到9字节而破坏了这种不变性,将导致格式不兼容。

因此,binary.PutUvarint的10字节最大长度是其设计哲学的一部分,即优先保证编码格式的通用性和扩展性,而非在所有情况下都追求极致的字节效率。

示例:binary.PutUvarint的编码行为

以下代码演示了binary.PutUvarint如何根据数值大小使用不同数量的字节进行编码:

package main

import (
    "encoding/binary"
    "fmt"
)

func main() {
    fmt.Println("--- binary.PutUvarint 变长编码示例 ---")

    // 较小的 uint64 值 (通常占用1个字节)
    val1 := uint64(150)
    buf1 := make([]byte, binary.MaxVarintLen64) // MaxVarintLen64 is 10
    n1 := binary.PutUvarint(buf1, val1)
    fmt.Printf("编码值 %d (0x%x): 占用 %d 字节, 编码结果: %x\n", val1, val1, n1, buf1[:n1])

    // 中等大小的 uint64 值
    val2 := uint64(123456789)
    buf2 := make([]byte, binary.MaxVarintLen64)
    n2 := binary.PutUvarint(buf2, val2)
    fmt.Printf("编码值 %d (0x%x): 占用 %d 字节, 编码结果: %x\n", val2, val2, n2, buf2[:n2])

    // 接近最大值的 uint64 值,且最高位(第63位)被设置
    // 2^63 - 1 (会占用9字节)
    val3 := uint64(1<<63 - 1)
    buf3 := make([]byte, binary.MaxVarintLen64)
    n3 := binary.PutUvarint(buf3, val3)
    fmt.Printf("编码值 %d (0x%x): 占用 %d 字节, 编码结果: %x\n", val3, val3, n3, buf3[:n3])

    // 最大 uint64 值 (2^64 - 1),会占用10字节
    val4 := ^uint64(0) // 2^64 - 1
    buf4 := make([]byte, binary.MaxVarintLen64)
    n4 := binary.PutUvarint(buf4, val4)
    fmt.Printf("编码值 %d (0x%x): 占用 %d 字节, 编码结果: %x\n", val4, val4, n4, buf4[:n4])

    // 一个会占用10字节的例子 (通常是高位bit被设置的值)
    val5 := uint64(1<<63) // 2^63
    buf5 := make([]byte, binary.MaxVarintLen64)
    n5 := binary.PutUvarint(buf5, val5)
    fmt.Printf("编码值 %d (0x%x): 占用 %d 字节, 编码结果: %x\n", val5, val5, n5, buf5[:n5])
}

运行上述代码,你将观察到不同数值的uint64被编码成不同长度的字节序列,其中最大值或高位被设置的值会占用10字节。

总结与注意事项

通过上述分析,我们可以得出以下关键结论:

  • 内存存储:Go语言中uint64类型变量在内存中始终占用8字节的固定空间。这是由Go语言规范保证的,与数值大小无关。
  • 序列化编码:当使用binary.PutUvarint等函数进行变长编码时,uint64值可能被编码为1到10个字节。这种变长编码是为了节省存储空间,其最大10字节的长度是Go语言在编码通用性与字节效率之间权衡的结果。

在实际开发中,理解这两种不同的“大小”概念至关重要:

  • 当你考虑内存布局、结构体大小或数组元素大小时,应关注uint64的固定8字节。
  • 当你进行数据序列化、网络传输或文件存储时,应关注binary.PutUvarint等函数生成的变长编码大小,尤其是在设计数据协议或计算传输开销时。

正确区分这两种存储和编码方式,有助于编写出更高效、更健壮的Go程序。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

306

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

197

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

189

2025.07.04

Go中Type关键字的用法
Go中Type关键字的用法

Go中Type关键字的用法有定义新的类型别名或者创建新的结构体类型。本专题为大家提供Go相关的文章、下载、课程内容,供大家免费下载体验。

234

2023.09.06

go怎么实现链表
go怎么实现链表

go通过定义一个节点结构体、定义一个链表结构体、定义一些方法来操作链表、实现一个方法来删除链表中的一个节点和实现一个方法来打印链表中的所有节点的方法实现链表。

446

2023.09.25

go语言编程软件有哪些
go语言编程软件有哪些

go语言编程软件有Go编译器、Go开发环境、Go包管理器、Go测试框架、Go文档生成器、Go代码质量工具和Go性能分析工具等。本专题为大家提供go语言相关的文章、下载、课程内容,供大家免费下载体验。

248

2023.10.13

0基础如何学go语言
0基础如何学go语言

0基础学习Go语言需要分阶段进行,从基础知识到实践项目,逐步深入。php中文网给大家带来了go语言相关的教程以及文章,欢迎大家前来学习。

698

2023.10.26

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号