【Golang】Go Slice(切片)原理及扩容机制解析

Go 切片原理及扩容机制解析

在 Go 语言中,切片(slice)作为一种灵活且高效的数据结构,广泛应用于日常开发。本文将深入解析 Go 切片的底层结构、初始化方式、共享机制以及扩容策略,并结合源码分析其内部实现细节,帮助你更好地理解和使用切片。


1. Go 切片的底层结构

Go 切片底层由一个结构体实现,该结构体包含指向底层数组的指针、切片的长度以及容量。其定义大致如下:

切片内存结构示意图

type slice struct {
   
    // 指向底层数组起始位置的指针
    array unsafe.Pointer
    // 当前切片的长度
    len   int
    // 底层数组的容量
    cap   int
}

2. 切片的初始化

Go 中可以通过多种方式初始化切片,常见的有以下几种方式:

2.1 直接声明

此时声明的切片为 nil 切片,不能直接使用其元素:

var s []int

2.2 使用 make 初始化

使用 make 函数可以创建指定长度和容量的切片:

  • 创建长度和容量均为 8 的切片:

    s := make([]int, 8)
    

    此时,切片中每个位置都已经被分配了默认值。

  • 指定长度为 8、容量为 16 的切片:

    s := make([]int, 8, 16)
    

    注意:可以访问 [0, len) 区间的元素,而访问 [len, cap) 的位置会触发 panic。

2.3 使用字面量进行初始化

直接通过字面量创建并赋值:

s := []int{
   1, 2, 3}

3. 切片的共享底层数组

需要注意的是,切片本身只是对底层数组的一个描述。当你对切片进行截取操作时,新切片仍然引用原有的底层数组,这意味着修改新切片的元素会影响原切片。

示例代码

package main

import (
    "fmt"
)

func main() {
   
    s := []int{
   1, 2, 3, 4, 5}
    s1 := s[1:]
    fmt.Println("s 的首地址: ", &s[0])
    fmt.Println("s1 的首地址: ", &s1[0])
    s1[0] = -1
    fmt.Println("修改后的 s: ", s)
}

输出示例:

s 的首地址:  0x1400012e000
s1 的首地址:  0x1400012e008
修改后的 s:  [1 -1 3 4 5]

如上所示,虽然 ss1 是两个不同的切片,但它们共享同一块底层数组:

切片共享底层数组示意图


4. Go 切片的扩容机制

当使用 append() 向切片中添加新元素时,如果切片的容量不足,Go 会自动扩容。扩容并非“刚好分配所需空间”,而是遵循一套优化策略,平衡性能和内存使用。下面将详细介绍扩容的原理和实现。
扩容流程

4.1 扩容预期

假设初始切片 s1 的容量为 3:

s1 := make([]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值