Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(1)

简介: Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】

1、数组

特别需要注意的是在 Go 语言中,数组长度也是数组类型的一部分!所以尽管元素类型相同但是长度不同的两个数组,它们的类型并不相同。

1.1、数组的初始化

1.1.1、通过初始化列表{}来设置值

    var arr [3]int // int类型的数组默认会初始化全为 0
    var addr = [3]string{"beijing","wuhan","shanghai"} // 指定初始化值

1.1.2、自动推断数组长度

var addr = [...]string{"beijing","wuhan","shanghai"}

1.1.3、通过索引初始化部分元素

    // 数组类型 [6]string
    var addr = [...]string{0:"beijing",3:"wuhan",5:"shanghai"}
    // 数组类型 [3]int
    nums := [...]int{0:1,2:3}

1.2、数组的遍历

1.2.1、一维数组的遍历

    var arr = [3]int{1,2,3}
    arr[1] = 3 // 通过索引修改数组的值
 
    for i:=0;i<len(arr);i++{
        fmt.Printf("%d ",arr[i])    // 1 3 3 
    }

1.2.2、二维数组的遍历

二维数组的定义

注意在二维数组中,列数必须指定,无法自动推导! 行数可以用 [...] 来自动推导。

    // 二维数组的初始化
    var table = [2][3]int{
        {1,2,3},
        {4,5,6}
    }
    
  fmt.Println(table) // [[1 2 3] [4 5 6]]
普通遍历

这种遍历方式就是利用索引来遍历:

for i := 0; i < len(table); i++ {
    for j := 0; j < len(table[i]); j++ {
      fmt.Print(table[i][j]," ")
    }
    fmt.Println()
  }
    // 1 2 3 
    // 4 5 6
使用 range 遍历

range 关键字是专门用来遍历数组或切片的,它会返回两个值:索引元素值

    for _,i := range table{
    for _,j := range i{
      fmt.Print(j," ")
    }
    fmt.Println()
  }

       对于索引我们不需要,所以直接赋值给 _ ,而外层的元素值 i 代表的是二维数组 table 的每一行,相当于是一个一维数组。

所以,对于上面的一维数组遍历,我们同样可以采用这种方式:

    for _,i := range arr{
        fmt.Print(i," ")
    }

1.3、数组是值类型的

      数组是值类型的!这是非常重要的一点。这意味着如果把数组作为参数传递给函数进行处理,那么实际的数组并不会发生改变。而且数组的容量是固定的,在定义时必须就确定!

       但是 Go 语言提供了一种可以引用类型的特殊数组——切片(slice)。对于切片 ,它不仅是引用类型(作为函数参数时,如果形参被修改,那么实参也将被修改),同时也是可动态扩容的,也就是说我们不需要向数组那样在声明时就初始化大小。

func main(){
    s1 := [2]int{0,0} // [0,0]
    fmt.Println(s1) // [0,0]
    addOne(s1)
    fmt.Println(s1) // [0,0]
}
func addOne(s [2]int){
    for i:=0;i<len(s);i++{
        s[i] += 1
    }
}

       可以看到,正因为数组是值类型的,所以当把数组传递给函数的时候,函数中操作的形参相当于是拷贝的这么一个数组,所以操作完毕之后实参并不受影响。(这里的形参在 Java 中就像存在 addOne 方法自己的栈区,而实参存在 mian 方法的栈区,所以操作的就不是同一个内存地址)

1.3.1、数组的比较

       也正因为数组是值类型的,所以它支持比较

    var arr1 = [3]int{1,2,3}
    var arr2 = [3]int{1,2,3}
    fmt.Println(arr1 == arr2) // true

可以看到,两个相同类型元素相同的数组是相同的。

2、切片

       正因为数组的长度固定,并且数组长度属于数组类型的一部分,所以使用数组非常局限。比如我们定义一个遍历数组的方法,那么我们必须指定数组的类型!

       比如下面这个方法只能遍历存放三个元素的数组:

func printArr(arr [3]int){
    for _,i := range arr{
        fmt.Print(i," ")
    }   
}

2.1、切片的定义

切片定义时不需要指定容量,所以也就没有什么初始值:

var arr [] string

       需要注意的一点是:因为切片是引用类型而不是值类型的,所以它不能直接比较(和其它切片用 == 进行比较,只能和 nil 进行比较

2.1.1、切片的长度和容量

       切片拥有自己的长度和容量,我们可以通过使用内建的 len() 函数求长度,使用内建的 cap() 函数求切片的容量。

  • 长度(Length):长度表示切片中当前可以访问的元素个数,即从第一个元素到最后一个元素的数目。
  • 容量(Capacity):容量是指切片的底层数组的大小,即从切片的第一个元素到底层数组的最后一个元素之间的元素总数。容量代表了在不重新分配内存的情况下,切片可以增长到的最大大小。

2.1.2、简单切片表达式

通过数组来给切片初始化:

s := a[low : high]

注意:切片表达式中的low和high表示一个索引范围()左包含,右不包含)

    var a = [5]int{1,2,3,4,5}
    s := a[1:3]
    fmt.Println(a) // [1,2,3,4,5]
    fmt.Println(s) // [2,3]
    fmt.Printf("cap(s)=%v,len(s)=%v",cap(s),len(s)) // cap(s)=4,len(s)=2

此外

  • s := a[1:] :代表从索引 [0,len(s))
  • s := a[:2]:代表从索引  [0,2)
  • s := a[:]:代表整个数组

注意:对于数组或字符串,如果 0 <= low <= high <= len(a),则索引合法,否则就会索引越界(out of range)。

2.1.3、完整切片表达式

完整的切片表达式是这样的:

s := a[low : high : max]

       除了比普通切片表达式多了一个参数 max (这个 max 的作用是将得到的结果切片的容量设置为 max-low),此外,在完整切片表达式中,只有第一个索引值 low 可以省略,它默认为 0.

    var a = [5]int{1,2,3,4,5}
    s := a[:3:5]
    fmt.Println(a) // [1,2,3,4,5]
    fmt.Println(s) // [1,2,3]
    fmt.Printf("cap(s)=%v,len(s)=%v",cap(s),len(s)) // cap(s)=5,len(s)=3

注意:完整切片表达式需要满足的条件是 0 <= low <= high <= max <= cap(a),其他条件和简单切片表达式相同。

2.1.4、使用 make 函数构造切片

我们上面都是基于已有的数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内建的 make()函数,格式如下:

// T: 切片元素类型
make([]T, size, cap)

比如:

    arr := make([]int,2,10) 
    fmt.Println(arr) // [0,0]
  fmt.Println(cap(arr)) // 10
  fmt.Println(len(arr)) // 2

上面代码中切片 arr 的内部存储空间已经分配了10个,但实际上只用了2个。 容量并不会影响当前元素的个数,所以len(a)返回2,cap(a)则返回该切片的容量 10。

注意:对于未开辟的空间是不能初始化赋值的,比如上面我们指定了 2 个长度,如果此时对 arr[3] := 1 进行赋值,那么会报错,因为切片必须使用特定的方法来进行元素的添加(append)。

2.1.5、切片的本质

切片的本质就是对底层数组的封装,它包含了三个信息:

  1. 底层数组的指针
  2. 切片的长度(len)
  3. 切片的容量(cap)
a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := a[:5]

s2 := a[3:6]


Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(2)https://developer.aliyun.com/article/1534258

相关文章
|
6天前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
44 1
|
6天前
|
存储 Java Go
【Golang】(3)条件判断与循环?切片和数组的关系?映射表与Map?三组关系傻傻分不清?本文带你了解基本的复杂类型与执行判断语句
在Go中,条件控制语句总共有三种if、switch、select。循环只有for,不过for可以充当while使用。如果想要了解这些知识点,初学者进入文章中来感受吧!
31 1
|
10月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
204 7
|
11月前
|
Go 索引
Go语言中,遍历数组或切片
在Go语言中,遍历数组或切片
234 6
|
11月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
137 3
|
8月前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
205 3
Go 语言入门指南:切片
|
7月前
|
JavaScript 前端开发 API
JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
array.map()可以用来数据转换、创建派生数组、应用函数、链式调用、异步数据流处理、复杂API请求梳理、提供DOM操作、用来搜索和过滤等,比for好用太多了,主要是写法简单,并且非常直观,并且能提升代码的可读性,也就提升了Long Term代码的可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
10月前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
298 62
|
11月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
11月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。

热门文章

最新文章