一.go数组和切片的区别(7个面试问题)

本文详细介绍了Go语言中数组和切片的区别,包括它们的初始化、长度、拷贝行为以及在函数传参中的表现。数组长度固定,传参为值传递;切片长度可变,传参为引用传递,且涉及到浅拷贝和深拷贝的概念。此外,还讨论了切片与数组的关系,以及如何从数组创建切片。

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

一.数组和切片的区别(7个问题)

对应问题

1.数组和切片的关系和区别(跟谁学,京东,伴鱼,腾讯+3,小米+3,百度,深信服,知乎,触宝,微步,北京神州,度小满,腾讯音乐,早安科技)

2.如何把数组转换成一个切片(跟谁学)

3.slice用copy和左值进行初始化的区别 (有色)--------copy的用法在下面

4.一个函数传参一个slice,先append在赋值和另外一个函数先赋值在append, 那个会发生变化?(字节)

5.slice分配在堆上还是栈上 (京东)

6.切片的比较,深拷贝的问题(北京合链)

7.make一个slice后返回的是指针吗? map呢?(字节)

1.数组和切片初始化的区别(问题1,2,3)

1.1数组的初始化

数组的初始化一共有两种方式

(1):采用的是 [x]type{a,b,c}

(2):采用的是[…]type{a,b,c}(自动计算数组的长度)

例子如下

//在定义的时候就确定长度  [x]type{a,b,c}
var arr0 = [3]int{1, 2, 3}
fmt.Println(arr0)

//在定义的时候省略长度   [...]type{a,b,c}
var arr1 = [...]int{1, 2, 3}
fmt.Println(arr1)

//在定义的时候制定下标对应的值
var arr2 = [...]int{3: 4, 4: 5}
fmt.Println(arr2)

//自定义结构的数组
var arr3 = [...]struct {
   name string
   age  uint8
}{
   {"xiaoming", 8}, 
   {"xiaojin", 16},
 }
fmt.Println(arr3)

1.2切片的初始化

切片的初始化方法就多了,一共有四种初始化的方式

(1): var arr1 [] int

(2):make([]type,len)

(3):make([]type,len,cap)

(4):从数组上面

//方法1 直接声明 (直接声明的话将是nil,也被成为nil切片)
	var arr1 []int

	if arr1 == nil {
		fmt.Println("空")
	} else {
		fmt.Println("非空")
	}
	fmt.Println(len(arr1))
	fmt.Println(cap(arr1))
/* 方法1 打印结果
  arr1空
  0
  0
*/

//	方法2 使用 :=
	arr2 := []int{}
	if arr2 == nil {
		fmt.Println("空")
	} else {
		fmt.Println("非空")
	}
	fmt.Println(len(arr2))
	fmt.Println(cap(arr2))
/* 方法2 打印结果
    arr2非空
    0
    0
*/

// 方法3 使用 make 创建  make([]type,len,cap)
// 3.1 make([]type,len)  省略cap 此时cap默认等于len
	var arr3_1 = make([]int, 5)
	if arr3_1 == nil {
		fmt.Println("arr3_1空")
	} else {
		fmt.Println("arr3_1非空")
	}
	fmt.Println(len(arr3_1))
	fmt.Println(cap(arr3_1))
//3.2 make([]type,len,cap)
	var arr3_2 = make([]int, 0, 5)
	if arr3_2 == nil {
		fmt.Println("arr3_2空")
	} else {
		fmt.Println("arr3_3非空")
	}
	fmt.Println(len(arr3_2))
	fmt.Println(cap(arr3_2))

/* 方法3 打印结果
    arr3_1非空
	5
	5
	arr3_3非空
	0
	5
*/

// 方法4 从数组切片
	arrTemp := [5]int{1, 2, 3, 4, 5}
	arr4 := arrTemp[:]
	if arr4 == nil {
		fmt.Println("arr4空")
	} else {
		fmt.Println("arr4非空")
	}
	fmt.Println(len(arr4))
	fmt.Println(cap(arr4))
/* 方法4 打印结果
    arr4非空
    5
    5
*/

结论:只有第一种方法(var arr []int)定义切片的时候,切片为空,而且如果slice==nil,len,cap的结果都为0

需要注意的是从数组切片的方法中可以有三个参数,详细如下(来自地鼠文档)

img

2.数组和切片在函数传参中的区别(问题3,4,5,6,7)

对应问题

一个函数传参一个slice,先append在赋值和另外一个函数先赋值在append, 那个会发生变化?(字节)

slice分配在堆上还是栈上 (京东)

切片的比较,深拷贝的问题(北京合链)

make一个slice后返回的是指针吗? map呢?(字节)

Go语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。

Golang中引用类型:指针,slice,map,channel,接口,函数等。变量存放的是一个内存地址值,这个地址值指向的空间存的才是最终的值。内存通常在堆中分配,当没有任何变量引用这个地址时,改地址对应的数据空间就成为一个垃圾,通过GC回收

1.简单的数据像 int,float,bool,string这样的基本类型都属于值类型,传参到函数后,在函数中做出的修改就是对副本进行了修改,到了函数外面还是原来的值

2.复杂的数据结构一般都是用引用类型保存,虽然传进去的是副本,但是副本指针依然指向的是同一块空间,故对函数中引用类型的修改会修改函数外面的值,到了函数外面,将会发生改变

2.1数组的函数传参以及复制(值传递)

//数组中的复制,以及函数中的传递都是值赋值的
func TestArrayTrans(t *testing.T) {
	arrayA := [2]int{100, 200}
	var arrayB [2]int

	arrayB = arrayA

	fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
	fmt.Printf("arrayB : %p , %v\n", &arrayB, arrayB)

    
    //在函数中对arrayA进行修改
	testArray(arrayA)

	//函数中修改后,A还是原来的值
	fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
    
	/* 打印结果:
	arrayA : 0xc00000a350 , [100 200]
	arrayB : 0xc00000a360 , [100 200]
	func Array : 0xc00000a390 , [100 200]
	三个打印的地址都不同(说明数组的传递是值传递的,即每次都是复制和传参中都是开辟一个新的内存地址,并一一复制),这样如果数组的长度较大的时候将会消耗大量的空间

	*/
	
}

func testArray(x [2]int) {
   fmt.Printf("func Array : %p , %v\n", &x, x)
   x[0] = 1
}

2.2切片的函数传参以及复制(引用传递)

//数组中的复制,以及函数中的传递都是引用赋值的
func TestSliceTrans(t *testing.T) {
	arrayA := []int{100, 200}

	var arrayB []int

	//浅拷贝,只是拷贝了指针,两个指针指向的却是同一块地址,对arrayA的改变会影响到arrayB
	//arrayB = arrayA
	
	//深拷贝,开辟了一个新的空间,两个指针指向的是不同的地址,此时对arrayA的改变不会影响到arrayB
	//注意:copy函数取决于两者中最短的那个slice,如果没有下面的两行append,arrayB的长度是0,那么arrayB的打印结果将为[]
	//arrayB = append(arrayB, 1)
	//arrayB = append(arrayB, 1)
	//copy(arrayB, arrayA)
	
	fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
	fmt.Printf("arrayB : %p , %v\n", &arrayB, arrayB)

	testSlice(arrayA)

	//函数中修改后,A还是原来的值
	fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
	fmt.Printf("arrayA : %p , %v\n", &arrayB, arrayB)

	/* 打印结果
	arrayA : 0xc0000040d8 , [100 200]
	arrayB : 0xc0000040f0 , [100 200]
	func Array : 0xc000004138 , [100 200]
	arrayA : 0xc0000040d8 , [1 200]
	arrayA : 0xc0000040f0 , [1 200]
	slice是引用类型的传递,地址虽然不同,但是只是两个不同地址的指针,指针指向的是同一块内存地址,在函数中进行的改变会影响到函数外面的结果,而且拷贝也只是拷贝了一份指针,两个指针指向同一块地址,对arrayA的修改,会影响到arrayB的值
	因此引用类型的拷贝一般又特定的函数来进行深拷贝,如slice中使用copy(dst,src)函数
	*/

}

2.3数组函数传参的小坑

[4]int和[3]int不被视为同一种类型,编译不通过

func TestArrayFuncParameter(t *testing.T) {
	numbersArray := [3]int{1, 2, 3}
	//编译不通过
	SumForArray(numbersArray)
}

func SumForArray(numbers [4]int) int {
	sum := 0
	for i := 0; i < 5; i++ {
		sum += numbers[i]
	}
	return sum
}

3.数组和切片的关系

func TestRelation(t *testing.T) {
   arr := [5]int{1, 2, 3, 4, 5}
   slice := arr[1:3]
   fmt.Println(len(slice), cap(slice))
}

示意图如下
在这里插入图片描述

结论:

数组

1.数组固定长度

2.数组需要指定大小,不指定的话可以通过[…]type,将进行自动推算出大小,数组长度是数据类型的一部分,当函数需要传参数[4]int 的时候,如果传入的参数是[3]int 编译不通过

3.数组是通过值传递的

切片

1.切片可以改变长度

2.数组不需要指定大小

3.切片是引用传递

笔记由阅读以下文档做出

地鼠文档https://www.topgoer.cn/docs/golang/chapter03-10

彻底理解Golang Slicehttps://mp.weixin.qq.com/s/Rqi_nSbw6oxEnpxZ8teCCw

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值