Go-切片类型详解(遍历、内存、追加、插入、删除等)_golang 数组头插入数据

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

目录

上篇文章思考题

简介

声明

声明并初始化

一般形式

引用数组

引用切片

遍历

for

for range

内存

函数/方法

长度与容量

追加与拷贝

拷贝

排序

插入与删除

函数传参

值传递

引用传递

注意事项

全部代码

结果截图

参考


上篇文章思考题

Go-数组类型详解

答案:

can not use nums(type [6]int) as type[5]int

注意:一个数组类型,包含元素类型和长度,不同长度,同样的元素也是不一样的类型。因此,今天的切片就很有意义。

简介

  • 切片是引用类型
  • 长度可以变化容量随长度变化
  • 是结构体–>可查看源代码

切片即动态数组,底层在当前数组不够用时,开辟更大的数组,拷贝后再增加元素。

声明

var 变量名 []type

func make(Type, size …IntegerType[,capacity]) Type

内建函数make分配并初始化一个类型为切片、映射、或通道的对象。其第一个实参为类型,而非值。make的返回类型与其参数相同,而非指向它的指针。其具体结果取决于具体的类型:

切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量;它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。

capacity可选默认为指定的长度,make底层也有数组,不可见

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

源代码查看

src->runtime->slice.go

func makeslice(et \*\_type, len, cap int) unsafe.Pointer {
	mem, overflow := math.MulUintptr(et.size, uintptr(cap))
	if overflow || mem > maxAlloc || len < 0 || len > cap {
		// NOTE: Produce a 'len out of range' error instead of a
		// 'cap out of range' error when someone does make([]T, bignumber).
		// 'cap out of range' is true too, but since the cap is only being
		// supplied implicitly, saying len is clearer.
		// See golang.org/issue/4085.
		mem, overflow := math.MulUintptr(et.size, uintptr(len))
		if overflow || mem > maxAlloc || len < 0 {
			panicmakeslicelen()
		}
		panicmakeslicecap()
	}

	return mallocgc(mem, et, true)
}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

切片声明代码

	var slice []int
	slice1 := make([]int,5)
	fmt.Println("slice slice1:",slice,slice1)

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

声明并初始化

一般形式

类似数组,直接写后面花括号里面,代码:

	slice2 := []int{1,2,3,4}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

引用数组

给出数组数据

	arr := [5]int{5,6,7,8,9}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

slice[start:end],默认:start=0,end =len(arr)

代码

	slice3 := arr[1:]

引用切片

和引用数组类似

	slice4 := slice3[1:]

切片及数组在内存的情况,请查看后序内存一节。

遍历

for

	for i:=0;i<len(slice3);i++{
		fmt.Print(slice3[i]," ")
	}

for range

	for _,v := range slice3{
		fmt.Print(v," ")
	}

内存

查看结构体的具体内容

src->reflect->type.go

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

编译时创建切片代码

src->cmd->compile->types

// NewSlice returns the slice Type with element type elem.
func NewSlice(elem \*Type) \*Type {
	if t := elem.Cache.slice; t != nil {
		if t.Elem() != elem {
			Fatalf("elem mismatch")
		}
		return t
	}

	t := New(TSLICE)
	t.Extra = Slice{Elem: elem}
	elem.Cache.slice = t
	return t
}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

	fmt.Printf("&slice1:%p,&slice1[0]:%v\n", &slice1, &slice1[0])
	fmt.Printf("&arr:%p &arr[1]:%v &slice3:%p &slice3[0]:%v\n",&arr,&arr[1],&slice3,&slice3[0])
	fmt.Printf("&slice4:%p &slice4[0]:%v\n",&slice4,&slice4[0])
	arr[2] = 99
	fmt.Println("slice3[1] slice4[0]",slice3[1],slice4[0])

slice1内存

arr、slice3、slice4内存

函数/方法

长度与容量

len、cap函数获取长度和容量

代码

	fmt.Println("len(slice3) cap(slice3) len(slice4) cap(slice4):", len(slice3), cap(slice3), len(slice4), cap(slice4))

追加与拷贝

append

func append(slice []Type, elems …Type) []Type

内建函数append将元素追加到切片的末尾。若它有足够的容量,其目标就会重新切片以容纳新的元素。否则,就会分配一个新的基本数组。append返回更新后的切片,因此必须存储追加后的结果。

查看slice增长源代码

src->runtime->slice.go

// 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.
// The new slice's length is set to the old slice's length,
// NOT to the new requested capacity.
// This is for codegen convenience. The old slice's length is used immediately
// to calculate where to write new values during an append.
// TODO: When the old backend is gone, reconsider this decision.
// The SSA backend might prefer the new length or to return only ptr/cap and save stack space.
func growslice(et \*\_type, old slice, cap int) slice {
	if raceenabled {
		callerpc := getcallerpc()
		racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
	}
	if msanenabled {
		msanread(old.array, uintptr(old.len*int(et.size)))
	}

	if cap < old.cap {
		panic(errorString("growslice: cap out of range"))
	}

	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
	if cap > doublecap {
		newcap = cap
	} else {
		if old.cap < 1024 {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < cap {
				newcap += newcap / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = cap
			}
		}
	}

	var overflow bool
	var lenmem, newlenmem, capmem uintptr
	// Specialize for common values of et.size.
	// For 1 we don't need any division/multiplication.
	// For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
	// For powers of 2, use a variable shift.
	switch {
	case et.size == 1:
		lenmem = uintptr(old.len)
		newlenmem = uintptr(cap)
		capmem = roundupsize(uintptr(newcap))
		overflow = uintptr(newcap) > maxAlloc
		newcap = int(capmem)
	case et.size == sys.PtrSize:
		lenmem = uintptr(old.len) * sys.PtrSize
		newlenmem = uintptr(cap) * sys.PtrSize
		capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
		overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
		newcap = int(capmem / sys.PtrSize)
	case isPowerOfTwo(et.size):
		var shift uintptr
		if sys.PtrSize == 8 {
			// Mask shift for better code generation.
			shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
		} else {
			shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
		}
		lenmem = uintptr(old.len) << shift
		newlenmem = uintptr(cap) << shift
		capmem = roundupsize(uintptr(newcap) << shift)
		overflow = uintptr(newcap) > (maxAlloc >> shift)
		newcap = int(capmem >> shift)
	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)
	}

	// The check of overflow in addition to capmem > maxAlloc is needed
	// to prevent an overflow which can be used to trigger a segfault
	// on 32bit architectures with this example program:
	//
	// type T [1<<27 + 1]int64
	//
	// var d T
	// var s []T
	//
	// func main() {
	// s = append(s, d, d, d, d)


![img](https://img-blog.csdnimg.cn/img_convert/c39a47374bce38510915a84c240381e9.png)
![img](https://img-blog.csdnimg.cn/img_convert/9c1193c51402b4af30adf0b9eddb803e.png)
![img](https://img-blog.csdnimg.cn/img_convert/8cd604a045491f4589edaa41d6e7eb75.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

n 32bit architectures with this example program:
	//
	// type T [1<<27 + 1]int64
	//
	// var d T
	// var s []T
	//
	// func main() {
	// s = append(s, d, d, d, d)


[外链图片转存中...(img-KfC223bW-1715805295765)]
[外链图片转存中...(img-rlJFcT1K-1715805295765)]
[外链图片转存中...(img-16XpCo9g-1715805295766)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Go语言中,有两种常见的方式可以遍历数组。第一种方式是使用for循环和索引的形式遍历数组。代码示例如下: ``` package main import ( "fmt" ) func main() { arr := [5]int{1, 2, 3, 4, 5} for i := 0; i < len(arr); i++ { fmt.Println(arr[i]) } } ``` 这种方式使用一个循环变量i来表示数组的索引,通过递增i的值来遍历数组元素。在每次循环中,通过`arr[i]`来获取数组中的元素,并进行相应的操作。 第二种方式是使用for range循环来遍历数组。代码示例如下: ``` package main import ( "fmt" ) func main() { arr := [5]int{1, 2, 3, 4, 5} for index, value := range arr { fmt.Println(index, value) } } ``` 这种方式中,for range循环会遍历数组中的每个元素,并将索引和对应的值分别赋给index和value变量。在每次循环中,可以直接使用index和value来操作数组元素。 总结起来,通过for循环加索引和for range循环是两种常见的在Go语言中遍历数组的方式。可以根据具体的需求选择合适的方式来进行数组遍历。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [go语言中遍历数组的方法有哪些](https://blog.csdn.net/yaxuan88521/article/details/129194466)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值