关于golang:go语言学习数组字符串和切片未完待续

95次阅读

共计 2613 个字符,预计需要花费 7 分钟才能阅读完成。

数组

Go 语言的数组是一种值类型,尽管数组的元素能够被批改,然而数组自身的赋值和函数传参都是以整体复制的形式解决的。

定义形式

var a [3]int                    // 定义长度为 3 的 int 型数组, 元素全副为 0
var b = [...]int{1, 2, 3}       // 定义长度为 3 的 int 型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2}    // 定义长度为 3 的 int 型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为 6 的 int 型数组, 元素为 1, 2, 0, 0, 5, 6

第一种形式是定义一个数组变量的最根本的形式,数组的 长度明确 指定,数组中的每个元素都以 零值初始化

第二种形式定义数组,能够在定义的时候程序 指定全副元素的初始化值,数组的长度依据初始化元素的数目主动计算。

第三种形式是以 索引 的形式来初始化数组的元素,因而元素的初始化值呈现程序比拟随便。这种初始化形式和 map[int]Type 类型的初始化语法相似。数组的长度以呈现的最大的索引为准,没有明确初始化的元素仍然用 0 值初始化。

第四种形式是 混合 了第二种和第三种的初始化形式,后面两个元素采纳程序初始化,第三第四个元素零值初始化,第五个元素通过索引初始化,最初一个元素跟在后面的第五个元素之后采纳程序初始化。

咱们还能够定义一个空的数组:

var d [0]int       // 定义一个长度为 0 的数组
var e = [0]int{}   // 定义一个长度为 0 的数组
var f = [...]int{} // 定义一个长度为 0 的数组

长度为 0 的数组在内存中并不占用空间。空数组尽管很少间接应用,然而能够用于强调某种特有类型的操作时防止调配额定的内存空间,比方用于管道的同步操作:

c1 := make(chan [0]int)
go func() {fmt.Println("c1")
    c1 <- [0]int{}}()
<-c1

在这里,咱们并不关怀管道中传输数据的实在类型,其中管道接管和发送操作只是用于音讯的同步。对于这种场景,咱们用空数组来作为管道类型能够缩小管道元素赋值时的开销。当然个别更偏向于用无类型的匿名构造体代替:

c2 := make(chan struct{})
go func() {fmt.Println("c2")
    c2 <- struct{}{} // struct{}局部是类型, {}示意对应的构造体值
}()
<-c2

数组的遍历

咱们能够用 for 循环来迭代数组。上面常见的几种形式都能够用来遍历数组:

// 索引遍历
for i := range a {fmt.Printf("a[%d]: %d\n", i, a[i])
}
// 索引 + 值
for i, v := range b {fmt.Printf("b[%d]: %d\n", i, v)
}
// 一般的 for 循环
for i := 0; i < len(c); i++ {fmt.Printf("c[%d]: %d\n", i, c[i])
}

用 for range 形式迭代的性能可能会更好一些,因为这种迭代能够保障不会呈现数组越界的情景,每轮迭代对数组元素的拜访时能够省去对下标越界的判断。

数组的内存构造

Go 语言中数组是值语义。一个数组变量即示意整个数组,它并不是隐式的指向第一个元素的指针(比方 C 语言的数组),而是一个残缺的值。

当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组。如果数组较大的话,数组的赋值也会有较大的开销。为了防止复制数组带来的开销,能够传递一个指向数组的指针,然而数组指针并不是数组。(尽管它简直能够间接当成数组应用)

var a = [...]int{1, 2, 3} // a 是一个数组
var b = &a                // b 是指向数组的指针

fmt.Println(a[0], a[1])   // 打印数组的前 2 个元素
fmt.Println(b[0], b[1])   // 通过数组指针拜访数组元素的形式和数组相似

for i, v := range b {     // 通过数组指针迭代数组的元素
    fmt.Println(i, v)
}

其中 b 是指向 a 数组的指针,然而通过 b 拜访数组中元素的写法和 a 相似的。

其余数组

数组不仅仅能够用于数值类型,还能够定义字符串数组、构造体数组、函数数组、接口数组、管道数组等等:// 字符串数组
var s1 = [2]string{"hello", "world"}
var s2 = [...]string{"你好", "世界"}
var s3 = [...]string{1: "世界", 0: "你好",}

// 构造体数组
var line1 [2]image.Point
var line2 = [...]image.Point{image.Point{X: 0, Y: 0}, image.Point{X: 1, Y: 1}}
var line3 = [...]image.Point{{0, 0}, {1, 1}}

// 图像解码器数组
var decoder1 [2]func(io.Reader) (image.Image, error)
var decoder2 = [...]func(io.Reader) (image.Image, error){
    png.Decode,
    jpeg.Decode,
}

// 接口数组
var unknown1 [2]interface{}
var unknown2 = [...]interface{}{123, "你好"}

// 管道数组
var chanList = [2]chan int{}

简而言之,所有雷同的数据结构都能够放在一起汇合为一个数组。

字符串

字符串通常是用来蕴含人类可读的文本数据。在 go 语言中,字符串的元素是 不可批改 的,实质上是一个 只读 的字节数组.

Go 语言的源代码要求是 UTF8 编码,导致 Go 源代码中呈现的字符串面值常量个别也是 UTF8 编码的。源代码中的文本字符串通常被解释为采纳 UTF8 编码的 Unicode 码点(rune)序列。

咱们也能够用字符串示意 GBK 等非 UTF8 编码的数据,不过这种时候将字符串看作是一个只读的二进制数组更精确,因为 for range 等语法并不能反对非 UTF8 编码的字符串的遍历。

底层构造

Go 语言字符串的底层构造在 reflect.StringHeader 中定义:

type StringHeader struct {
    Data uintptr
    Len  int
}

字符串构造由两个信息组成:第一个是字符串指向的底层字节数组,第二个是字符串的字节的长度。字符串其实是一个构造体,因而字符串的赋值操作也就是 reflect.StringHeader 构造体的复制过程,并不会波及底层字节数组的复制。能够将字符串数组看作一个构造体数组。

正文完
 0