189 8069 5689

go语言slice评分 Go slice

go语言中slice和array的区别

go 是瞬间发生的动作,since 则表示从以前到现在的一段持续时间,而瞬间动作不可能持续发生,只要把瞬间动作 go 否定为 not/never go

目前创新互联已为近1000家的企业提供了网站建设、域名、雅安服务器托管网站托管运营、企业网站设计、偃师网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

go语言数组,切片和字典的区别和联系

、数组 

与其他大多数语言类似,Go语言的数组也是一个元素类型相同的定长的序列。

(1)数组的创建。

数组有3种创建方式:[length]Type 、[N]Type{value1, value2, ... , valueN}、[...]Type{value1, value2, ... , valueN} 如下:

复制代码代码如下:

func test5() {

var iarray1 [5]int32

var iarray2 [5]int32 = [5]int32{1, 2, 3, 4, 5}

iarray3 := [5]int32{1, 2, 3, 4, 5}

iarray4 := [5]int32{6, 7, 8, 9, 10}

iarray5 := [...]int32{11, 12, 13, 14, 15}

iarray6 := [4][4]int32{{1}, {1, 2}, {1, 2, 3}}

fmt.Println(iarray1)

fmt.Println(iarray2)

fmt.Println(iarray3)

fmt.Println(iarray4)

fmt.Println(iarray5)

fmt.Println(iarray6)

}

结果:

[0 0 0 0 0]

[1 2 3 4 5]

[1 2 3 4 5]

[6 7 8 9 10]

[11 12 13 14 15]

[[1 0 0 0] [1 2 0 0] [1 2 3 0] [0 0 0 0]]

我们看数组 iarray1,只声明,并未赋值,Go语言帮我们自动赋值为0。再看 iarray2 和 iarray3 ,我们可以看到,Go语言的声明,可以表明类型,也可以不表明类型,var iarray3 = [5]int32{1, 2, 3, 4, 5} 也是完全没问题的。

(2)数组的容量和长度是一样的。cap() 函数和 len() 函数均输出数组的容量(即长度)。如:

复制代码代码如下:

func test6() {

iarray4 := [5]int32{6, 7, 8, 9, 10}

fmt.Println(len(iarray4))

fmt.Println(cap(iarray4))

}

输出都是5。

(3)使用:

复制代码代码如下:

func test7() {

iarray7 := [5]string{"aaa", `bb`, "可以啦", "叫我说什么好", "()"}

fmt.Println(iarray7)

for i := range iarray7 {

fmt.Println(iarray7[i])

}

}

二、切片

Go语言中,切片是长度可变、容量固定的相同的元素序列。Go语言的切片本质是一个数组。容量固定是因为数组的长度是固定的,切片的容量即隐藏数组的长度。长度可变指的是在数组长度的范围内可变。

(1)切片的创建。

切片的创建有4种方式:

1)make ( []Type ,length, capacity )

2) make ( []Type, length)

3) []Type{}

4) []Type{value1 , value2 , ... , valueN }

从3)、4)可见,创建切片跟创建数组唯一的区别在于 Type 前的“ [] ”中是否有数字,为空,则代表切片,否则则代表数组。因为切片是长度可变的。如下是创建切片的示例:

复制代码代码如下:

func test8() {

slice1 := make([]int32, 5, 8)

slice2 := make([]int32, 9)

slice3 := []int32{}

slice4 := []int32{1, 2, 3, 4, 5}

fmt.Println(slice1)

fmt.Println(slice2)

fmt.Println(slice3)

fmt.Println(slice4)

}

输出为:

[0 0 0 0 0]

[0 0 0 0 0 0 0 0 0]

[]

[1 2 3 4 5]

如上,创造了4个切片,3个空切片,一个有值的切片。

(2)切片与隐藏数组:

一个切片是一个隐藏数组的引用,并且对于该切片的切片也引用同一个数组。如下示例,创建了一个切片slice0,并根据这个切片创建了2个切片 slice1 和 slice2:

复制代码代码如下:

func test9() {

slice0 := []string{"a", "b", "c", "d", "e"}

slice1 := slice0[2 : len(slice0)-1]

slice2 := slice0[:3]

fmt.Println(slice0, slice1, slice2)

slice2[2] = "8"

fmt.Println(slice0, slice1, slice2)

}

输出为:

[a b c d e] [c d] [a b c]

[a b 8 d e] [8 d] [a b 8]

可见,切片slice0 、 slice1 和 slice2是同一个底层数组的引用,所以slice2改变了,其他两个都会变。

(3)遍历、修改切片:

复制代码代码如下:

func test10() {

slice0 := []string{"a", "b", "c", "d", "e"}

fmt.Println("\n~~~~~~元素遍历~~~~~~")

for _, ele := range slice0 {

fmt.Print(ele, " ")

ele = "7"

}

fmt.Println("\n~~~~~~索引遍历~~~~~~")

for index := range slice0 {

fmt.Print(slice0[index], " ")

}

fmt.Println("\n~~~~~~元素索引共同使用~~~~~~")

for index, ele := range slice0 {

fmt.Print(ele, slice0[index], " ")

}

fmt.Println("\n~~~~~~修改~~~~~~")

for index := range slice0 {

slice0[index] = "9"

}

fmt.Println(slice0)

}

如上,前三种循环使用了不同的for range循环,当for后面,range前面有2个元素时,第一个元素代表索引,第二个元素代表元素值,使用 “_” 则表示忽略,因为go语言中,未使用的值会导致编译错误。

只有一个元素时,该元素代表索引。

只有用索引才能修改元素。如在第一个遍历中,赋值ele为7,结果没有作用。因为在元素遍历中,ele是值传递,ele是该切片元素的副本,修改它不会影响原本值,而在第四个遍历——索引遍历中,修改的是该切片元素引用的值,所以可以修改。

结果为:

~~~~~~元素遍历~~~~~~

a b c d e

~~~~~~索引遍历~~~~~~

a b c d e

~~~~~~元素索引共同使用~~~~~~

aa bb cc dd ee

~~~~~~修改~~~~~~

[9 9 9 9 9]

(4)、追加、复制切片:

复制代码代码如下:

func test11() {

slice := []int32{}

fmt.Printf("slice的长度为:%d,slice为:%v\n", len(slice), slice)

slice = append(slice, 12, 11, 10, 9)

fmt.Printf("追加后,slice的长度为:%d,slice为:%v\n", len(slice), slice)

slicecp := make([]int32, (len(slice)))

fmt.Printf("slicecp的长度为:%d,slicecp为:%v\n", len(slicecp), slicecp)

copy(slicecp, slice)

fmt.Printf("复制赋值后,slicecp的长度为:%d,slicecp为:%v\n", len(slicecp), slicecp)

}

追加、复制切片,用的是内置函数append和copy,copy函数返回的是最后所复制的元素的数量。

(5)、内置函数append

内置函数append可以向一个切片后追加一个或多个同类型的其他值。如果追加的元素数量超过了原切片容量,那么最后返回的是一个全新数组中的全新切片。如果没有超过,那么最后返回的是原数组中的全新切片。无论如何,append对原切片无任何影响。如下示例:

复制代码代码如下:

func test12() {

slice := []int32{1, 2, 3, 4, 5, 6}

slice2 := slice[:2]

_ = append(slice2, 50, 60, 70, 80, 90)

fmt.Printf("slice为:%v\n", slice)

fmt.Printf("操作的切片:%v\n", slice2)

_ = append(slice2, 50, 60)

fmt.Printf("slice为:%v\n", slice)

fmt.Printf("操作的切片:%v\n", slice2)

}

如上,append方法用了2次,结果返回的结果完全不同,原因是第二次append方法追加的元素数量没有超过 slice 的容量。而无论怎样,原切片slice2都无影响。结果:

slice为:[1 2 3 4 5 6]

操作的切片:[1 2]

slice为:[1 2 50 60 5 6]

操作的切片:[1 2]

go程序如何分配堆栈的

在Go语言中有一些调试技巧能帮助我们快速找到问题,有时候你想尽可能多的记录异常但仍觉得不够,搞清楚堆栈的意义有助于定位Bug或者记录更完整的信息。

本文将讨论堆栈跟踪信息以及如何在堆栈中识别函数所传递的参数。

Functions

先从这段代码开始:

Listing 1

01 package main

02

03 func main() {

04     slice := make([]string, 2, 4)

05     Example(slice, "hello", 10)

06 }

07

08 func Example(slice []string, str string, i int) {

09     panic("Want stack trace")

10 }

Example函数定义了3个参数,1个string类型的slice, 1个string和1个integer, 并且抛出了panic,运行这段代码可以看到这样的结果:

Listing 2

Panic: Want stack trace

goroutine 1 [running]:

main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:9 +0x64

main.main()

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:5 +0x85

goroutine 2 [runnable]:

runtime.forcegchelper()

/Users/bill/go/src/runtime/proc.go:90

runtime.goexit()

/Users/bill/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 3 [runnable]:

runtime.bgsweep()

/Users/bill/go/src/runtime/mgc0.go:82

runtime.goexit()

/Users/bill/go/src/runtime/asm_amd64.s:2232 +0x1

堆栈信息中显示了在panic抛出这个时间所有的goroutines状态,发生的panic的goroutine会显示在最上面。

Listing 3

01 goroutine 1 [running]:

02 main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:9 +0x64

03 main.main()

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:5 +0x85

第1行显示最先发出panic的是goroutine 1, 第二行显示panic位于main.Example中, 并能定位到该行代码,在本例中第9行引发了panic。

下面我们关注参数是如何传递的:

Listing 4

// Declaration

main.Example(slice []string, str string, i int)

// Call to Example by main.

slice := make([]string, 2, 4)

Example(slice, "hello", 10)

// Stack trace

main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

这里展示了在main中带参数调用Example函数时的堆栈信息,比较就能发现两者的参数数量并不相同,Example定义了3个参数,堆栈中显示了6个参数。现在的关键问题是我们要弄清楚它们是如何匹配的。

第1个参数是string类型的slice,我们知道在Go语言中slice是引用类型,即slice变量结构会包含三个部分:指针、长度(Lengthe)、容量(Capacity)

Listing 5

// Slice parameter value

slice := make([]string, 2, 4)

// Slice header values

Pointer:  0x2080c3f50

Length:   0x2

Capacity: 0x4

// Declaration

main.Example(slice []string, str string, i int)

// Stack trace

main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

因此,前面3个参数会匹配slice, 如下图所示:

Figure 1

figure provided by Georgi Knox

我们现在来看第二个参数,它是string类型,string类型也是引用类型,它包括两部分:指针、长度。

Listing 6

// String parameter value

"hello"

// String header values

Pointer: 0x425c0

Length:  0x5

// Declaration

main.Example(slice []string, str string, i int)

// Stack trace

main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

可以确定,堆栈信息中第4、5两个参数对应代码中的string参数,如下图所示:

Figure 2

figure provided by Georgi Knox

最后一个参数integer是single word值。

Listing 7

// Integer parameter value

10

// Integer value

Base 16: 0xa

// Declaration

main.Example(slice []string, str string, i int)

// Stack trace

main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

现在我们可以匹配代码中的参数到堆栈信息了。

Figure 3

figure provided by Georgi Knox

Methods

如果我们将Example作为结构体的方法会怎么样呢?

Listing 8

01 package main

02

03 import "fmt"

04

05 type trace struct{}

06

07 func main() {

08     slice := make([]string, 2, 4)

09

10     var t trace

11     t.Example(slice, "hello", 10)

12 }

13

14 func (t *trace) Example(slice []string, str string, i int) {

15     fmt.Printf("Receiver Address: %p\n", t)

16     panic("Want stack trace")

17 }

如上所示修改代码,将Example定义为trace的方法,并通过trace的实例t来调用Example。

再次运行程序,会发现堆栈信息有一点不同:

Listing 9

Receiver Address: 0x1553a8

panic: Want stack trace

01 goroutine 1 [running]:

02 main.(*trace).Example(0x1553a8, 0x2081b7f50, 0x2, 0x4, 0xdc1d0, 0x5, 0xa)

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:16 +0x116

03 main.main()

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:11 +0xae

首先注意第2行的方法调用使用了pointer receiver,在package名字和方法名之间多出了"*trace"字样。另外,参数列表的第1个参数标明了结构体(t)地址。我们从堆栈信息中看到了内部实现细节。

Packing

如果有多个参数可以填充到一个single word, 则这些参数值会合并打包:

Listing 10

01 package main

02

03 func main() {

04     Example(true, false, true, 25)

05 }

06 

07 func Example(b1, b2, b3 bool, i uint8) {

08     panic("Want stack trace")

09 }

这个例子修改Example函数为4个参数:3个bool型和1个八位无符号整型。bool值也是用8个bit表示,所以在32位和64位架构下,4个参数可以合并为一个single word。

Listing 11

01 goroutine 1 [running]:

02 main.Example(0x19010001)

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:8 +0x64

03 main.main()

/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/

temp/main.go:4 +0x32

这是本例的堆栈信息,看下图的具体分析:

Listing 12

// Parameter values

true, false, true, 25

// Word value

Bits    Binary      Hex   Value

00-07   0000 0001   01    true

08-15   0000 0000   00    false

16-23   0000 0001   01    true

24-31   0001 1001   19    25

// Declaration

main.Example(b1, b2, b3 bool, i uint8)

// Stack trace

main.Example(0x19010001)

以上展示了参数值是如何匹配到4个参数的。当我们看到堆栈信息中包括十六进制值,需要知道这些值是如何传递的。

go语言中实现切片(slice)的三种方式

定义一个切片,然后让切片去引用一个已经创建好的数组。基本语法如下:

索引1:切片引用的起始元素位

索引2:切片只引用该元素位之前的元素

例程如下:

在该方法中,我们未指定容量cap,这里的值为5是系统定义的。

在方法一中,可以用arr数组名来操控数组中的元素,也可以通过slice切片来操控数组中的元素。切片是直接引用数组,数组是事先存在的,程序员是可见的。

通过 make 来创建切片,基本语法如下:

make函数第三个参数cap即容量是可选的,如果一定要自己注明的话,要注意保证cap≥len。

用该方法可以 指定切片的大小(len)和容量(cap)

例程如下:

由于未赋值系统默认将元素值置为0,即:

数值类型数组:    默认值为 0

字符串数组:       默认值为 ""

bool数组:           默认值为 false

在方法二中,通过make方式创建的切片对应的数组是由make底层维护,对外不可见,即只能通过slice去访问各个元素。

定义一个切片,直接就指定具体数组,使用原理类似于make的方式。

例程如下:

Go语言中数组和slice的区别

PHP的数组是数列Array,列表List,散列表/关联数组/字典Hashtable的聚合体。

是一个非常高级的数据结构。也是一个优秀的设计。

有一套数组功能函数支持php的数组。

C数组只是一个"固定长度、固定类型"的数列Array,实现简单,功能原始。有数列的随机操作快的长处,也有数列的增、删低效的毛病

如果要比较,PHP的数组应该和C++的STL有一比,功能类似。


网页名称:go语言slice评分 Go slice
标题URL:http://cdxtjz.cn/article/dodhdsg.html

其他资讯