内部实现

数组是 元素类型相同长度固定 的存储在连续内存中 数据类型。长度固定,是数组和切片最明显的区别。
数组存储的类型可以是内置类型,比如整型或者字符串,也可以是自定义的数据结构。

概念

  • 数组长度是数组类型的一部分
  • 数组在go中为值类型
  • 数组的指针和指针数组
    • 指针数组指的是数组的元素是指针
    • 数组的指针
  • new方法创建指向数组的指针
  • 占用连续的内存,索引访问的效率非常高

声明和初始化

声明原则

  1. 指明存储的 元素的数量
  2. 指明存储的 数据的类型
    1. var <varname> [n]<type>, n>0
    以上声明了一个数组array,但是还没有对他进行初始化,这时候数组array里面的值,是对应元素类型的零值,也就是说,现在这个数组是5个0。
    数组一旦声明后,其元素类型和大小都不能变,如果还需要存储更多的元素只能通过创建一个新的数组,然后把原来数组的数据复制过去。

    初始化

    刚刚声明的数组已经被默认的元素类型零值初始化了,可以采用如下办法再次进行初始化:
    1. var array [5]int // [0 0 0 0 0]
    2. array = [5]int{1,2,3,4,5} // [1 2 3 4 5]
    3. // 或者
    4. var array [5]int = [5]int{1,2,3,4,5}
    这两步比较繁琐,Go为我们提供了:=操作符,可以让我们在创建数组的时候直接初始化。
    1. array := [5]int{1,2,3,4,5}

这种简短变量声明的方式不仅适用于数组,还适用于任何数据类型,这也是Go语言中常用的方式。

自动计算数组的长度

有时候我们更懒,连数组的长度都不想指定,不过没有关系,使用…代替就好了,Go会自动推导出数组的长度。

  1. array := [...]int{1,2,3,4,5}

假如我们只想给索引为1和3的数组初始化相应的值,其他都为0怎么做呢,直接的办法有:

  1. array := [5]int{0,1,0,4,0}

还有一种更好的办法,上面讲默认初始化为零值,那么我们就可以利用这个特性,只初始化索引1和3的值:

  1. array := [5]int{1:1,3:4}

使用数组

访问元素

数组的访问非常简单,通过索引即可,操作符为[]

  1. array := [5]int{1:1,3:4}
  2. fmt.Printf("%d",array[1])

修改元素

  1. array:=[5]int{1:1,3:4}
  2. fmt.Printf("%d\n",array[1])
  3. array[1] = 3
  4. fmt.Printf("%d\n",array[1])

遍历数组 传统方法

  1. func main() {
  2. array := [5]int{1: 1, 3: 4}
  3. for i := 0; i < 5; i++ {
  4. fmt.Printf("索引:%d,值:%d\n", i, array[i])
  5. }
  6. }

遍历数组 go风格方法

使用for range 循环

  1. func main() {
  2. array := [5]int{1: 1, 3: 4}
  3. for i, v := range array {
  4. fmt.Printf("索引:%d,值:%d\n", i, v)
  5. }
  6. }

二维数组

声明

  1. var <varname> [m][n]<type>, n>0

声明并定义

  1. var <varname> [m][n]<type> = {{x,x,x,x,x},{x,x,x,x,x}}

len(array) 行数 len(array[0]) 列数

同样类型的数组

同样类型的数组是可以相互赋值的,不同类型的不行,会编译错误。那么什么是呢?Go语言规定,必须是长度一样,并且每个元素的类型一样的数组,才是同样类型的数组。

  1. array := [5]int{1: 1, 3: 4}
  2. var array1 [5]int = array //success
  3. var array2 [4]int = array1 //error

指针数组

和数组本身差不多,只不过元素类型是指针。

  1. array := [5]*int{1: new(int), 3:new(int)}

这样就创建了一个指针数组,并且为索引1和3都创建了内存空间,其他索引是指针的零值nil,这时候我们要修改指针变量的值也很简单,如下即可:

  1. array := [5]*int{1: new(int), 3:new(int)}
  2. *array[1] = 1

以上需要注意的是,只可以给索引1和3赋值,因为只有它们分配了内存,才可以赋值,如果我们给索引0赋值,运行的时候,会提示无效内存或者是一个nil指针引用。

  1. panic: runtime error: invalid memory address or nil pointer dereference

要解决这个问题,我们要通过array[0] =new(int)给索引0分配内存 ,然后再进行赋值修改。

  1. array := [5]*int{1: new(int), 3:new(int)}
  2. array[0] = new(int)
  3. *array[0] = 2
  4. fmt.Println(*array[0])

函数间传递数组

数组是值类型,故在函数间是值传递。

  1. func main() {
  2. array := [5]int{1: 2, 3:4}
  3. modify(array)
  4. fmt.Println(array) # 值不变
  5. }
  6. func modify(a [5]int){
  7. a[1] = 3
  8. fmt.Println(a)
  9. }

数组的指针传递

  1. func modify(a *[5]int){
  2. (*a)[1] = 3
  3. a[3] = 4 //两种格式
  4. }
  5. func main() {
  6. array := [5]int{1: 2, 3:4}
  7. modify(&array)
  8. fmt.Println(array)
  9. }

这是传递数组的指针的例子,会发现数组被修改了。所以这种情况虽然节省了复制的内存,但是要谨慎使用,因为一不小心,就会修改原数组,导致不必要的问题。

也可以通过函数的返回值实现数组的修改 这里注意,数组的指针和指针数组是两个概念,数组的指针是*[5]int,指针数组是[5]*int,注意*的位置。

数组的比较

长度相同可以比较

经常会见到: p , *p , &p 三个符号
&是取地址运算符,&p就是取指针p的地址。
p是一个指针变量的名字,表示此指针变量指向的内存地址,如果使用%p来输出的话,它将是一个16进制数。
*p表示此指针指向的内存地址中存放的内容,一般是一个和指针类型一致的变量或者常量。

练习

冒泡排序

  1. func Bubble() {
  2. var array [7]int = [7]int{7, 5, 1, 9, 0, 2, 8}
  3. for i := 0; i <len(array); i++ {
  4. for j := 0; j <len(array) - i - 1; j++ {
  5. if array[j] > array[j+1] {
  6. array[j], array[j+1] = array[j+1], array[j]
  7. }
  8. }
  9. }
  10. fmt.Println(array)
  11. }