返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

8.3 slice

发布于 2024-10-12 19:16:08 字数 3039 浏览 0 评论 0 收藏 0

切片底层数组未必在堆上分配。

只要合法,编译器尝试在栈上折腾。

// slice.go

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}
// slice.go

func makeslice(et *_type, len, cap int) unsafe.Pointer

    // 在堆上分配内存。
    mem, overflow := math.MulUintptr(et.size, uintptr(cap))

    return mallocgc(mem, et, true)
}

扩容

当超出容量(cap)限制时,会引发扩容,重新分配底层数组。

// growslice handles slice growth during append.
// It is passed the slice element type, the old slice, and the desired new minimum capacity,
// and it returns a new slice with at least that capacity, with the old data
// copied into it.

func growslice(et *_type, old slice, cap int) slice {
    
    if et.size == 0 {
        // append should not create a slice with nil pointer but non-zero len.
        // We assume that append doesn't need to preserve old.array in this case.
        return slice{unsafe.Pointer(&zerobase), old.len, cap}
    }
    
    newcap := old.cap
    doublecap := newcap + newcap
    
    // 如果 cap > 2x,那么按 cap 分配。
    // 如果元素 < 1024,那么 2x 分配。
    // 如果元素 >= 1024,按 1/4 递增,直到 > cap 申请。
    
    if cap > doublecap {
        newcap = cap
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            
            // Set newcap to the requested cap when
            // the newcap calculation overflowed.
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
    
    // 计算内存长度。
    switch {
    case ...: ...
    default:
       lenmem = uintptr(old.len) * et.size
       newlenmem = uintptr(cap) * et.size
       capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
       capmem = roundupsize(capmem)
       newcap = int(capmem / et.size)
    }
    
    // 分配新内存,复制数据。
    if et.ptrdata == 0 {
        p = mallocgc(capmem, nil, false)
    } else {
        p = mallocgc(capmem, et, true)
    }
    
    memmove(p, old.array, lenmem)
    
    return slice{p, old.len, newcap}
}

拷贝

从源和目标长度里,选择短的作为要复制数据量。

func slicecopy(toPtr unsafe.Pointer, toLen int, fmPtr unsafe.Pointer, fmLen int, width uintptr) int {

    // 没有数据要复制。
    if fmLen == 0 || toLen == 0 {
        return 0
    }
    
    // 选择短的作为复制长度。
    n := fmLen
    if toLen < n {
        n = toLen
    }
    
    // 如果元素宽度为 0(比如空结构),无需复制。
    if width == 0 {
        return n
    }
    
    size := uintptr(n) * width
    if size == 1 {
        *(*byte)(toPtr) = *(*byte)(fmPtr) // known to be a byte pointer
    } else {
        memmove(toPtr, fmPtr, size)
    }
    
    return n
}
func slicestringcopy(toPtr *byte, toLen int, fm string) int {    
    if len(fm) == 0 || toLen == 0 {
        return 0
    }
    
    n := len(fm)
    if toLen < n {
        n = toLen
    }

    memmove(unsafe.Pointer(toPtr), stringStructOf(&fm).str, uintptr(n))
    return n
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文