Go语言,slice切片,函数参数传递与扩容原理

本文深入探讨Go语言中切片的内部结构,详细解析其作为函数参数时的行为,以及append操作对切片的影响。重点阐述了切片扩容的源码实现,包括初始容量设置、不同扩容策略及其条件。通过实例分析,帮助读者理解切片在内存管理和并发安全中的关键角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Go语言,slice切片,函数参数传递与扩容原理

一 、源码

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

1.指针array,指向底层数组中切片指定的开始位置

2.长度len,即切片的长度

3.容量cap,也就是最大长度,即切片开始位置到数组的最后位置的长度

二、切片作为函数参数时

切片作为函数参数时是值传递,会复制一份副本过去。

如果改变原有数据,原切片数据是否会发生变化:会!

原因:尽管是值传递,但因为切片的第一个成员为指针,和原切片指向同一位置,所以会改变。

如果使用append追加数据,原切片数据是否会改变:不会!

原因:append()函数返回值为新的切片,新的切片的指针、len、cap,并不影响原切片的指针、len、cap,所以原切片不会变长,不会追加新的元素,原切片不会改变。

如果使用append()返回的新切片,改变这个新切片的前一部分数据(老数据,追加前的数据),原切片可能发生改变,因为扩容后的新切片如果是在原位置扩容,指针指向的起始位置没有改变,那么就会。

如果扩容后的新切片指针指向了新的起始位置,那么就不会。

扩容原理,附源码

	newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		const threshold = 256
		if old.cap < threshold {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < cap {
				// Transition from growing 2x for small slices
				// to growing 1.25x for large slices. This formula
				// gives a smooth-ish transition between the two.
				newcap += (newcap + 3*threshold) / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = cap
			}
		}
	}

初始化newcap=old.cap

如果所需容量大于原容量二倍,扩为所需容量

否则:{ 如果原容量小于256,扩为原容量的二倍,否则循环按照newcap+=(newcap+3*256)/4,直到newcap>=cap为止}

参考
参考1
参考2

<think>好的,用户问的是Go语言slice除了对数组切片还能做什么。我需要先回忆一下Goslice相关知识,然后组织答案。首先,slice的基础功能是对数组的封装,提供动态大小的视图,但除此之外,slice还有更多用途。 用户可能已经知道slice的基本切片操作,但想了解更高级的用法。我应该列举几个主要的功能,比如动态扩容、作为动态数组使用、传递引用、内存管理、函数结合、其他数据结构配合等。 需要确保每个点都解释清楚,并且例子准确。比如动态扩容方面,要提到append函数以及容量增长的机制。在作为动态数组使用时,说明如何直接创建slice而无需数组。传递引用时强调slice的指针特性,避免大数据的拷贝。 还要注意用户可能的实际应用场景,比如处理可变长度数据、高效传递数据、内存优化等。此外,可能存在的注意事项,比如多个slice共享底层数组导致的数据覆盖问题,需要提一下,帮助用户避免错误。 需要检查例子中的语法是否正确,比如使用make初始化slice,append的用法,copy函数的作用等。同时,保持语言简洁,结构清晰,分点说明,让用户容易理解。</think>在 Go 语言中,slice切片)除了对数组进行切片操作外,还有以下重要功能和用途: --- ### 1. **动态扩容数组** - **无需预先定义固定长度**:通过 `append` 函数可动态扩展容量。 - **示例**: ```go s := []int{1, 2, 3} // 直接创建切片(无需依赖数组) s = append(s, 4) // 添加元素后可能触发底层数组扩容 ``` - 扩容机制:当容量足时,会创建新数组并复制数据(通常按 2 倍扩容)。 --- ### 2. **作为轻量级动态数组使用** - **直接操作数据序列**:切片本身就是对底层数组的引用,可直接用于存储可变长度数据。 - **示例**: ```go names := []string{"Alice", "Bob"} // 独立切片 names = append(names, "Charlie") // 动态扩展 ``` --- ### 3. **高效传递数据** - **引用语义**:切片包含指向底层数组的指针,传递切片时只传递指针、长度和容量,避免大数组拷贝。 - **示例**: ```go func process(data []int) { // 直接操作原底层数组 } ``` --- ### 4. **内存复用优化** - **复用底层数组**:通过 `[:0]` 重置切片长度,复用已分配内存。 - **示例**: ```go buf := make([]byte, 1024) buf = buf[:0] // 重置长度,后续可重用 buf 的容量 ``` --- ### 5. **函数配合实现灵活操作** - **内置函数支持**: - `len(slice)` 获取长度 - `cap(slice)` 获取容量 - `copy(dst, src)` 复制切片内容 - `make([]T, len, cap)` 预分配切片 - **示例**: ```go a := []int{1, 2, 3} b := make([]int, 2) copy(b, a) // b 变为 [1, 2] ``` --- ### 6. **其他数据结构配合** - **模拟栈/队列**: ```go stack := []int{} stack = append(stack, 1) // 入栈 top := stack[len(stack)-1] // 查看栈顶 stack = stack[:len(stack)-1] // 出栈 ``` - **构建动态多维结构**: ```go matrix := make([][]int, 3) for i := range matrix { matrix[i] = make([]int, 4) // 每行独立切片 } ``` --- ### 7. **高效字符串处理** - ** `[]byte` 转换**: ```go str := "hello" bytes := []byte(str) // 转为字节切片 bytes[0] = 'H' newStr := string(bytes) // 转回字符串(输出 "Hello") ``` --- ### 注意事项 - **共享底层数组风险**:多个切片可能共享同一底层数组,修改数据时需注意相互影响。 - **扩容成本**:频繁 `append` 可能触发多次扩容,影响性能,可预分配容量优化。 通过灵活使用 slice,可以高效管理动态数据集合,是 Go 语言中处理序列化数据的核心工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值