0

0

Go语言中切片指针的预分配与填充:惯用方法解析

DDD

DDD

发布时间:2025-10-13 12:43:14

|

200人浏览过

|

来源于php中文网

原创

Go语言中切片指针的预分配与填充:惯用方法解析

本文深入探讨了在go语言中如何以惯用的方式预分配和填充包含指针的切片。通过分析`make`函数对切片长度和容量的影响,以及`append`操作的行为,文章指出了常见的误区。我们提供了两种主要的解决方案:一是通过直接索引来填充已预设长度的切片,二是利用`make`函数预设容量并结合`append`操作来高效构建切片。

在Go语言中,切片(slice)是构建动态数组的强大工具。然而,当涉及到预分配并填充包含指针的切片时,如果不理解make函数和append操作的底层机制,可能会导致非预期的结果。本文将详细阐述Go语言中预分配和填充切片的惯用方法。

理解make与append的行为

在Go中,make函数用于创建切片、映射或通道。对于切片,它有以下两种常用形式:

  1. make([]Type, length): 创建一个长度为length的切片,其所有元素都会被初始化为Type的零值。对于指针类型*Type,零值是nil。切片的容量(capacity)默认与长度相等。
  2. make([]Type, length, capacity): 创建一个长度为length,容量为capacity的切片。同样,所有元素会被初始化为Type的零值。

append函数用于向切片追加元素。它总是将新元素添加到切片的末尾,并返回一个新的切片(如果底层数组需要重新分配)。append操作会增加切片的长度,但不会改变已存在元素的索引或值。

常见误区示例

考虑以下代码,尝试预分配一个包含5个*UselessStruct指针的切片,然后使用append填充:

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

package main

import "fmt"

type UselessStruct struct {
    a int
    b int
}

func main() {
    mySlice := make([]*UselessStruct, 5) // 长度为5,元素为5个nil指针
    for i := 0; i != 5; i++ {
        mySlice = append(mySlice, &UselessStruct{}) // 追加新元素
    }

    fmt.Println(mySlice)
}

这段代码的输出是:[ 0xc0... 0xc0... 0xc0... 0xc0... 0xc0...]。 可以看到,前5个元素仍然是nil,append操作并没有替换这些nil值,而是将新的UselessStruct指针追加到了切片的末尾,导致切片的长度变成了10。

如果切片存储的是结构体值而非指针,情况类似:

package main

import "fmt"

type UselessStruct struct {
    a int
    b int
}

func main() {
    mySlice := make([]UselessStruct, 5) // 长度为5,元素为5个零值UselessStruct
    for i := 0; i != 5; i++ {
        mySlice = append(mySlice, UselessStruct{}) // 追加新元素
    }

    fmt.Println(mySlice)
}

输出将是:[{0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0}]。 同样,前5个元素是零值结构体,后续追加的才是新的结构体。

这表明,当使用make([]Type, length)创建一个具有非零长度的切片时,切片已经包含了length个元素(零值)。append操作只会在此基础上增加新元素,而不会覆盖已存在的元素。

惯用解决方案

Go语言提供了两种惯用的方法来正确地预分配和填充切片,具体取决于你的需求:

Elser AI Comics
Elser AI Comics

一个免费且强大的AI漫画生成工具,助力你三步创作自己的一出好戏

下载

方案一:直接索引填充(适用于已知固定长度)

当你事先知道切片的最终长度,并且希望填充所有元素时,最直接和惯用的方法是使用make([]Type, length)创建切片,然后通过索引直接为每个位置赋值。

package main

import "fmt"

type UselessStruct struct {
    a int
    b int
}

func main() {
    const size = 5
    mySlice := make([]*UselessStruct, size) // 创建一个长度为5的切片,元素为nil指针

    // 通过索引直接填充每个位置
    for i := range mySlice {
        mySlice[i] = new(UselessStruct) // 为每个位置分配并赋值一个新的UselessStruct指针
        // 或者使用 &UselessStruct{} 也可以
        // mySlice[i] = &UselessStruct{}
    }

    fmt.Println(mySlice)
    // 预期输出:[0xc0... 0xc0... 0xc0... 0xc0... 0xc0...] (5个不同的指针)
}

在这个例子中:

  • make([]*UselessStruct, size)创建了一个长度为size的切片,其中包含size个nil指针。
  • for i := range mySlice遍历切片的索引。
  • mySlice[i] = new(UselessStruct)在每个索引位置上创建了一个新的UselessStruct实例,并将其地址赋值给mySlice[i]。new(UselessStruct)和&UselessStruct{}都用于创建结构体实例的指针,它们在大多数情况下是等效的。

这种方法简洁明了,直接表达了“我需要一个固定大小的切片,并用特定值填充它”的意图。

方案二:预设容量与append结合(适用于动态增长但需优化性能)

当你需要动态地向切片添加元素,但又希望通过预分配内存来避免频繁的底层数组重新分配(这会带来性能开销)时,可以使用make([]Type, 0, capacity)来创建切片。这会创建一个长度为0但预留了capacity空间(底层数组)的切片。

package main

import "fmt"

type UselessStruct struct {
    a int
    b int
}

func main() {
    const capacity = 5
    // 创建一个长度为0,但容量为5的切片
    mySlice := make([]*UselessStruct, 0, capacity)

    // 使用append追加元素
    for i := 0; i < capacity; i++ {
        mySlice = append(mySlice, &UselessStruct{}) // 追加新的UselessStruct指针
    }

    fmt.Println(mySlice)
    // 预期输出:[0xc0... 0xc0... 0xc0... 0xc0... 0xc0...] (5个不同的指针)
}

在这个例子中:

  • make([]*UselessStruct, 0, capacity)创建了一个空切片,但底层数组已分配了容纳capacity个元素的空间。
  • append操作会将新元素添加到切片的末尾。由于切片有足够的容量,前capacity次append操作不会导致底层数组的重新分配,从而提高了效率。
  • 每次append都会增加切片的长度。

这种方法适用于你不知道最终长度,但可以预估一个大致容量的场景,以优化性能。

总结与注意事项

  • make([]Type, length):创建一个包含length个零值元素的切片。如果你想填充所有这些元素,应该使用索引mySlice[i] = value来赋值。
  • make([]Type, 0, capacity):创建一个空切片,但预留了capacity的内存空间。如果你打算通过append动态添加元素,并希望优化性能,这是首选方式。
  • append操作:append总是将元素添加到切片的末尾,并增加切片的长度。它不会覆盖或替换切片中已存在的元素。
  • 指针类型与值类型:无论是切片指针还是切片值,上述原则都适用。对于指针切片,你通常会分配新的对象并获取其地址(如new(Type)或&Type{});对于值切片,你直接提供值即可。

理解这些基本概念是编写高效、惯用Go代码的关键。选择哪种方法取决于你的具体需求:如果长度固定且已知,直接索引填充更简洁;如果长度动态且需要性能优化,预设容量并结合append是更好的选择。

相关专题

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

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

197

2025.06.09

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

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

189

2025.07.04

length函数用法
length函数用法

length函数用于返回指定字符串的字符数或字节数。可以用于计算字符串的长度,以便在查询和处理字符串数据时进行操作和判断。 需要注意的是length函数计算的是字符串的字符数,而不是字节数。对于多字节字符集,一个字符可能由多个字节组成。因此,length函数在计算字符串长度时会将多字节字符作为一个字符来计算。更多关于length函数的用法,大家可以阅读本专题下面的文章。

922

2023.09.19

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语言相关的文章、下载、课程内容,供大家免费下载体验。

249

2023.10.13

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

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

698

2023.10.26

Go语言实现运算符重载有哪些方法
Go语言实现运算符重载有哪些方法

Go语言不支持运算符重载,但可以通过一些方法来模拟运算符重载的效果。使用函数重载来模拟运算符重载,可以为不同的类型定义不同的函数,以实现类似运算符重载的效果,通过函数重载,可以为不同的类型实现不同的操作。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

194

2024.02.23

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号