Go Array

Array 数组

1.初始化

存储结构

编译期间的数组类型:

type Array struct {
	Elem  *Type // element type
	Bound int64 // number of elements; <0 if unknown yet
}

cmd/compile/internal/types.NewArray 函数生成的,该类型包含两个字段,分别是元素类型 Elem 和数组的大小 Bound,这两个字段共同构成了数组类型,而当前数组是否应该在堆栈中初始化也在编译期就确定了。

func NewArray(elem *Type, bound int64) *Type {
   if bound < 0 {
      base.Fatalf("NewArray: invalid bound %v", bound)
   }
   t := New(TARRAY)
   t.Extra = &Array{Elem: elem, Bound: bound}
   t.SetNotInHeap(elem.NotInHeap())
   if elem.HasTParam() {
      t.SetHasTParam(true)
   }
   return t
}
array初始化方式

**编译期函数遍历阶段完成对数组的初始化:**数组是内存中一片连续的区域,需要在初始化时被指定长度,数 组的大小取决于数组中存放的元素大小。对于数组还有一种语法糖, 可以不用指定类型,如arr3:=[…]int{2,3,4},这种声明方式在 编译时自动推断长度。

  • 字面量初始化

    var arr = [4]int{1,2,3,4}
    arr1 := [4]int{1,2,3,4}
    
  • 定长空数组

    var arr2 = [4]int{}
    
  • 语法糖自动推断长度

    arr3 := [...]int{2,3,4}
    
  • 使用new创建数组

    arr4 := new([3]int)
    

anylit函 数用于处理各种类型的字面量。当数组的长度小于4时,在运行时数组 会被放置在栈中,如果当前数组的元素大于四个,cmd\compile\internal\walk\complit.go\anylit 会先获取一个唯一的 staticname,然后调用cmd\compile\internal\walk\complit.go.fixedlit 函数在静态存储区初始化数组中的元素并将临时变量赋值给数组:

image-20220318125607592

编译时初始化:
  1. 声明一块连续的内存空间

  2. 将字面量值依次放入数组中 fixedlit函数将执行数组初始化与赋值的逻辑。

    fixedlit函数伪代码:

    var arr = [3]int{1, 2, 3}
    // 变为:
    var arr [3]int
    arr[0] = 1
    arr[1] = 2
    arr[2] = 3
    

2. 数组的可比性

  1. Go 语言数组在初始化之后大小就无法改变。
  2. 存储元素类型相同、但是大小不同的数组类型在 Go 语言看来也是完全不同的,只有两个条件都相同才是同一类型,也就是说数组的大小也作为类型的描述故 [3]int[5]int 完全是不同的类型。
测试能否对数组重新赋值

如下代码段,企图通过赋值改变arr2数组大小

func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}// arr1为具有5个int元素的数组
    arr2 := [3]int{1, 2, 3} // arr2为具有3个int元素的数组
    arr1 = arr2
    arr1 = [3]int{}
}

使用Go提供的 test 命令对该段代码测试,可以看出无论是使用arr2数组或是空数组对arr1都会在编译期报错:

image-20220318140830230

测试数组的可比性

存在如下4个数组

arr1 := [5]int{1, 2, 3, 4, 5}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{1, 2, 3}
arr4 := [3]int{4, 4, 4}

尝试使用ifarr1arr2进行对比:

func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}
    arr2 := [3]int{1, 2, 3}
    arr3 := [3]int{1, 2, 3}
    arr4 := [3]int{4, 4, 4}
    if arr1 == arr2 {

    }
}

使用Go提供的 test 命令对该段代码测试:

因为arr1与arr2不是相同的类型所以无法比较

image-20220318141909700

如下代码测试结果为:

true

false

说明,仅当两个数组类型(元素类型、数组大小)且对应下标的元素值相同时,比较结果才为true

func main() {
    arr2 := [3]int{1, 2, 3}
    arr3 := [3]int{1, 2, 3}
    arr4 := [3]int{2, 2, 3}
    if arr3 == arr2 {
        fmt.Println("true")
    } else {
        fmt.Println("false")
    }

    if arr4 == arr2 {
        fmt.Println("true")
    } else {
        fmt.Println("false")
    }
}

3.数组值复制

与C语言中的数组显著不同的是,Go语言中的数组在赋值和函数调 用时的形参都是值复制。

如下代码段

func ChangeArray(arr [3]int) {
	arr[0] = 99
	fmt.Printf("In func arr Addr: %p Value: %v\n", &arr, arr)
}
func main() {
    arr2 := [3]int{1, 2, 3}
	temp := arr2
	fmt.Printf("temp Addr: %p Value: %v\n", &temp, temp)
	fmt.Printf("Befor func arr2 Addr: %p Value: %v\n", &arr2, arr2)
	ChangeArray(arr2)
	fmt.Printf("After func arr2 Addr: %p Value: %v\n", &arr2, arr2) 
}

执行结果:赋值操作、函数传递都会触发数组复制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJ4peu05-1648342979285)(https://raw.githubusercontent.com/Voryla/myimages/main/deepinJVM/20220318143612.png)]

4.数组的越界访问问题

数组访问越界是非常严重的错误,Go语言中对越界的判断有一部 分是在编译期间类型检查阶段完成的,typecheck1函数会对访问数组 的索引进行验证。

数组越界的判定分为三种情况:

  1. 访问数组的索引是非整数时报错为non-integer array index%v。
  2. 访问数组的索引是负数时报错为invalid array index%v(index must be non-negative)。
  3. 访问数组的索引越界时报错为invalid array index%v(out of bounds for%d-element array)。

总结

​ 数组在初始化时,在不考虑逃逸分析的情况下,如果数组中元素的个数小于或者等于 4 个,那么所有的变量会直接在栈上初始化,如果数组元素大于 4 个,变量就会在静态存储区初始化然后拷贝到栈上;在初始化之后,无法更改数组的大小。仅当两个数组类型(元素类型、数组大小)且对应下标的元素值相同时,比较结果才为true;赋值操作和函数传递都会触发数组复制。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值