golang-101-hacks(12)——切片作为函数参数传递注:本文是对 golang-101-hacks 中文翻译 。
在Go语言中go语言slice评分,函数参数是值传递 。使用slice作为函数参数时,函数获取到go语言slice评分的是slicego语言slice评分的副本:一个指针 , 指向底层数组的起始地址 , 同时带有slice的长度和容量 。既然各位熟知数据存储的内存的地址,现在可以对切片数据进行修改 。让我们看看下面的例子:
In Go, the function parameters are passed by value. With respect to use slice as a function argument, that means the function will get the copies of the slice: a pointer which points to the starting address of the underlying array, accompanied by the length and capacity of the slice. Oh boy! Since you know the address of the memory which is used to store the data, you can tweak the slice now. Let's see the following example:
运行结果如下
由此可见,执行modifyValue函数,切片s的元素发生了变化 。尽管modifyValue函数只是操作slice的副本,但是任然改变了切片的数据元素,看另一个例子:
You can see, after running modifyValue function, the content of slice s is changed. Although the modifyValue function just gets a copy of the memory address of slice's underlying array, it is enough!
See another example:
The result is like this:
而这一次,addValue函数并没有修改main函数中的切片s的元素 。这是因为它只是操作切片s的副本,而不是切片s本身 。所以如果真的想让函数改变切片的内容 , 可以传递切片的地址:
This time, the addValue function doesn't take effect on the s slice in main function. That's because it just manipulate the copy of the s, not the "real" s.
So if you really want the function to change the content of a slice, you can pass the address of the slice:
运行结果如下
go程序如何分配堆栈的在Go语言中有一些调试技巧能帮助我们快速找到问题go语言slice评分 , 有时候你想尽可能多的记录异常但仍觉得不够go语言slice评分,搞清楚堆栈的意义有助于定位Bug或者记录更完整的信息 。
本文将讨论堆栈跟踪信息以及如何在堆栈中识别函数所传递的参数 。
Functions
先从这段代码开始:
Listing 1
01 package main
02
03 func main() {
04slice := make([]string, 2, 4)
05Example(slice, "hello", 10)
06 }
07
08 func Example(slice []string, str string, i int) {
09panic("Want stack trace")
10 }
Example函数定义go语言slice评分了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:90x64
main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:50x85
goroutine 2 [runnable]:
runtime.forcegchelper()
/Users/bill/go/src/runtime/proc.go:90
runtime.goexit()
/Users/bill/go/src/runtime/asm_amd64.s:22320x1
goroutine 3 [runnable]:
runtime.bgsweep()
/Users/bill/go/src/runtime/mgc0.go:82
runtime.goexit()
/Users/bill/go/src/runtime/asm_amd64.s:22320x1
堆栈信息中显示了在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:90x64
03 main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:50x85
第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() {
08slice := make([]string, 2, 4)
09
10var t trace
11t.Example(slice, "hello", 10)
12 }
13
14 func (t *trace) Example(slice []string, str string, i int) {
15fmt.Printf("Receiver Address: %p\n", t)
16panic("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:160x116
03 main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:110xae
首先注意第2行的方法调用使用了pointer receiver,在package名字和方法名之间多出了"*trace"字样 。另外,参数列表的第1个参数标明了结构体(t)地址 。我们从堆栈信息中看到了内部实现细节 。
Packing
如果有多个参数可以填充到一个single word, 则这些参数值会合并打包:
Listing 10
01 package main
02
03 func main() {
04Example(true, false, true, 25)
05 }
06
07 func Example(b1, b2, b3 bool, i uint8) {
08panic("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:80x64
03 main.main()
/Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
temp/main.go:40x32
这是本例的堆栈信息,看下图的具体分析:
Listing 12
// Parameter values
true, false, true, 25
// Word value
BitsBinaryHexValue
00-070000 000101true
08-150000 000000false
16-230000 000101true
24-310001 10011925
// 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详解更多Gogo语言slice评分的相关文章发布在go语言slice评分我go语言slice评分的个人博客上go语言slice评分 , 欢迎访问
【go语言slice评分 go 语言 性能】关于go语言slice评分和go 语言 性能的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。
推荐阅读
- 柳州钓青鱼直播网红,柳州钓青鱼直播网红视频
- pg数据库怎么查建表日期,pg查看建表sql
- 关于sapsod的信息
- c语言自定义乘法函数 c语言自定义函数阶乘
- jsajax提交前判断,submit提交判断内容
- 苹果一体机怎么插u盘,苹果一体机如何插u盘
- iapp游戏开发免会员,app游戏开发费用
- python中sin函数的简单介绍
- 皇马巴黎直播软件叫什么,皇马vs巴黎圣日耳曼直播