From 4d4f007560b43ac026c2686c31dd6b63b96f840f Mon Sep 17 00:00:00 2001 From: "Xingyu.Wang" Date: Sun, 19 Aug 2018 21:08:18 +0800 Subject: [PATCH 1/3] PRF:20180712 Slices from the ground up.md @pityonline @name1e5s --- .../20180712 Slices from the ground up.md | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/translated/tech/20180712 Slices from the ground up.md b/translated/tech/20180712 Slices from the ground up.md index 29b52561cc..f03b22da5b 100644 --- a/translated/tech/20180712 Slices from the ground up.md +++ b/translated/tech/20180712 Slices from the ground up.md @@ -1,4 +1,4 @@ -从零开始学习切片 +从零开始学习 Go 语言的切片 ====== 这篇文章受到了我与同事讨论使用切片slice作为stack的一次聊天的启发。后来话题聊到了 Go 语言中的切片是如何工作的。我认为这些信息对别人也有用,所以就把它记录了下来。 @@ -23,14 +23,14 @@ } ``` - 语句 `b := a` 定义了一个类型是 `[5]int` 的新变量 `b`,然后把 `a` 中的内容 _复制_ 到 `b` 中。改变 `b` 对 `a` 中的内容没有影响,因为 `a` 和 `b` 是相互独立的值。[^1] + 语句 `b := a` 定义了一个类型是 `[5]int` 的新变量 `b`,然后把 `a` 中的内容 _复制_ 到 `b` 中。改变 `b` 对 `a` 中的内容没有影响,因为 `a` 和 `b` 是相互独立的值。^注1 ### 切片 Go 语言的切片和数组的主要有如下两个区别: -1. 切片没有一个固定的长度。切片的长度不是它类型定义的一部分,而是由切片内部自己维护的。我们可以使用内置的 `len` 函数知道它的长度。[^2] -2. 将一个切片赋值给另一个切片时 _不会_ 对切片进行复制操作。这是因为切片没有直接保存它的内部数据,而是保留了一个指向 _底层数组_ [^3] 的指针。数据都保留在底层数组里。 +1. 切片没有一个固定的长度。切片的长度不是它类型定义的一部分,而是由切片内部自己维护的。我们可以使用内置的 `len` 函数知道它的长度。^注2 +2. 将一个切片赋值给另一个切片时 _不会_ 对切片内容进行复制操作。这是因为切片没有直接持有其内部数据,而是保留了一个指向 _底层数组_ ^注3 的指针。数据都保留在底层数组里。 基于第二个特性,两个切片可以享有共同的底层数组。看下面的示例: @@ -71,7 +71,7 @@ Go 语言的切片和数组的主要有如下两个区别: } ``` - 在这个例子里,`a` 作为形参 `s` 的实参传进了 `negate` 函数,这个函数遍历 `s` 内的元素并改变其符号。尽管 `nagate` 没有返回值,且没有接触到 `main` 函数里的 `a`。但是当将之传进 `negate` 函数内时,`a` 里面的值却被改变了。 + 在这个例子里,`a` 作为形参 `s` 的实参传进了 `negate` 函数,这个函数遍历 `s` 内的元素并改变其符号。尽管 `nagate` 没有返回值,且没有访问到 `main` 函数里的 `a`。但是当将之传进 `negate` 函数内时,`a` 里面的值却被改变了。 大多数程序员都能直观地了解 Go 语言切片的底层数组是如何工作的,因为它与其它语言中类似数组的工作方式类似。比如下面就是使用 Python 重写的这一小节的第一个示例: @@ -99,11 +99,11 @@ irb(main):004:0> a => [1, 2, 0, 4, 5] ``` -在大多数将数组视为对象或者是引用类型的语言也是如此。[^4] +在大多数将数组视为对象或者是引用类型的语言也是如此。^注4 ### 切片头 -切片同时拥有值和指针特性的神奇之处在于理解切片实际上是一个结构体struct类型。这个结构体通常叫做 _切片头_,这里是[反射reflect包内的相关定义][7]。切片头的定义大致如下: +切片同时拥有值和指针特性的神奇之处在于理解切片实际上是一个结构体struct类型。通常在[反射reflect包内相应部分之后][7]的这个结构体被称作切片头slice header。切片头的定义大致如下: ![](https://dave.cheney.net/wp-content/uploads/2018/07/slice.001-300x257.png) @@ -155,9 +155,9 @@ func main() { } ``` -Go 的切片是作为值传递的这一点很是不寻常。当你在 Go 内定义一个结构体时,90% 的时间里传递的都是这个结构体的指针[^5]。切片的传递方式真的很不寻常,我能想到的唯一与之相同的例子只有 `time.Time`。 +Go 的切片是作为值传递而不是指针这一点不太寻常。当你在 Go 内定义一个结构体时,90% 的时间里传递的都是这个结构体的指针^注5 。切片的传递方式真的很不寻常,我能想到的唯一与之相同的例子只有 `time.Time`。 -切片作为值传递而不是作为指针传递这一特殊行为会让很多想要理解切片的工作原理的 Go 程序员感到困惑,这是可以理解的。你只需要记住,当你对切片进行赋值,取切片,传参或者作为返回值等操作时,你是在复制切片头结构的三个字段:指向底层数组的指针,长度,以及容量。 +切片作为值传递而不是作为指针传递这一特殊行为会让很多想要理解切片的工作原理的 Go 程序员感到困惑。你只需要记住,当你对切片进行赋值、取切片、传参或者作为返回值等操作时,你是在复制切片头结构的三个字段:指向底层数组的指针、长度,以及容量。 ### 总结 @@ -182,7 +182,7 @@ func main() { } ``` -在 `main` 函数的最开始我们把一个 `nil` 切片以及 `level` 的值 0 传给了函数 `f`。在函数 `f` 里我们把当前的 `level` 添加到切片的后面,之后增加 `level` 的值并进行递归。一旦 `level` 大于 5,函数返回,打印出当前的 `level` 以及它们复制到的 `s` 的内容。 +在 `main` 函数的最开始我们把一个 `nil` 切片传给了函数 `f` 作为 `level` 0 。在函数 `f` 里我们把当前的 `level` 添加到切片的后面,之后增加 `level` 的值并进行递归。一旦 `level` 大于 5,函数返回,打印出当前的 `level` 以及它们复制到的 `s` 的内容。 ``` level: 5 slice: [0 1 2 3 4 5] @@ -193,7 +193,7 @@ level: 1 slice: [0 1] level: 0 slice: [0] ``` -你可以注意到在每一个 `level` 内 `s` 的值没有被别的 `f` 的调用影响,尽管当计算更高的 `level` 时作为 `append` 的副产品,调用栈内的四个 `f` 函数创建了四个底层数组[^6],但是没有影响到当前各自的切片。 +你可以注意到在每一个 `level` 内 `s` 的值没有被别的 `f` 的调用影响,尽管当计算更高的 `level` 时作为 `append` 的副产品,调用栈内的四个 `f` 函数创建了四个底层数组^注6 ,但是没有影响到当前各自的切片。 ### 扩展阅读 @@ -204,12 +204,12 @@ level: 0 slice: [0] ### 注释 -[^1]: 这不是数组才有的特性,在 Go 语言里中 _一切_ 赋值都是复制过去的。 -[^2]: 你也可以在对数组使用 `len` 函数,但是得到的结果是多少人尽皆知。 -[^3]: 有时也叫做后台数组backing array,以及更不严谨的说法是后台切片。 -[^4]: Go 语言里我们倾向于说值类型以及指针类型,因为 C++ 的引用reference类型这个词产生误会。但在这里我认为调用数组作为引用类型是没有问题的。 -[^5]: 如果你的结构体有[定义在其上的方法或者用于满足某个接口][7],那么你传入结构体指针的比率可以飙升到接近 100%。 -[^6]: 证明留做习题。 +- 注1:这不是数组才有的特性,在 Go 语言里中 _一切_ 赋值都是复制过去的。 +- 注2:你也可以在对数组使用 `len` 函数,但是其结果本来就人尽皆知。 +- 注3:有时也叫做后台数组backing array,以及更不严谨的说法是后台切片。 +- 注4:Go 语言里我们倾向于说值类型以及指针类型,因为 C++ 的引用reference类型这个词产生误会。但在这里我认为调用数组作为引用类型是没有问题的。 +- 注5:如果你的结构体有[定义在其上的方法或者用于满足某个接口][7],那么你传入结构体指针的比率可以飙升到接近 100%。 +-注 6:证明留做习题。 ### 相关文章: From f234a54890c4f83820b6f93d25f79cf354531275 Mon Sep 17 00:00:00 2001 From: "Xingyu.Wang" Date: Sun, 19 Aug 2018 22:15:02 +0800 Subject: [PATCH 2/3] PRF:20180712 Slices from the ground up.md @pityonline --- .../20180712 Slices from the ground up.md | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/translated/tech/20180712 Slices from the ground up.md b/translated/tech/20180712 Slices from the ground up.md index f03b22da5b..f631cbfe1d 100644 --- a/translated/tech/20180712 Slices from the ground up.md +++ b/translated/tech/20180712 Slices from the ground up.md @@ -23,14 +23,14 @@ } ``` - 语句 `b := a` 定义了一个类型是 `[5]int` 的新变量 `b`,然后把 `a` 中的内容 _复制_ 到 `b` 中。改变 `b` 对 `a` 中的内容没有影响,因为 `a` 和 `b` 是相互独立的值。^注1 + 语句 `b := a` 定义了一个类型是 `[5]int` 的新变量 `b`,然后把 `a` 中的内容 _复制_ 到 `b` 中。改变 `b` 对 `a` 中的内容没有影响,因为 `a` 和 `b` 是相互独立的值。[^1] ### 切片 Go 语言的切片和数组的主要有如下两个区别: -1. 切片没有一个固定的长度。切片的长度不是它类型定义的一部分,而是由切片内部自己维护的。我们可以使用内置的 `len` 函数知道它的长度。^注2 -2. 将一个切片赋值给另一个切片时 _不会_ 对切片内容进行复制操作。这是因为切片没有直接持有其内部数据,而是保留了一个指向 _底层数组_ ^注3 的指针。数据都保留在底层数组里。 +1. 切片没有一个固定的长度。切片的长度不是它类型定义的一部分,而是由切片内部自己维护的。我们可以使用内置的 `len` 函数知道它的长度。[^2] +2. 将一个切片赋值给另一个切片时 _不会_ 对切片内容进行复制操作。这是因为切片没有直接持有其内部数据,而是保留了一个指向 _底层数组_ [^3] 的指针。数据都保留在底层数组里。 基于第二个特性,两个切片可以享有共同的底层数组。看下面的示例: @@ -99,7 +99,7 @@ irb(main):004:0> a => [1, 2, 0, 4, 5] ``` -在大多数将数组视为对象或者是引用类型的语言也是如此。^注4 +在大多数将数组视为对象或者是引用类型的语言也是如此。[^4] ### 切片头 @@ -155,7 +155,7 @@ func main() { } ``` -Go 的切片是作为值传递而不是指针这一点不太寻常。当你在 Go 内定义一个结构体时,90% 的时间里传递的都是这个结构体的指针^注5 。切片的传递方式真的很不寻常,我能想到的唯一与之相同的例子只有 `time.Time`。 +Go 的切片是作为值传递而不是指针这一点不太寻常。当你在 Go 内定义一个结构体时,90% 的时间里传递的都是这个结构体的指针[^5] 。切片的传递方式真的很不寻常,我能想到的唯一与之相同的例子只有 `time.Time`。 切片作为值传递而不是作为指针传递这一特殊行为会让很多想要理解切片的工作原理的 Go 程序员感到困惑。你只需要记住,当你对切片进行赋值、取切片、传参或者作为返回值等操作时,你是在复制切片头结构的三个字段:指向底层数组的指针、长度,以及容量。 @@ -193,7 +193,7 @@ level: 1 slice: [0 1] level: 0 slice: [0] ``` -你可以注意到在每一个 `level` 内 `s` 的值没有被别的 `f` 的调用影响,尽管当计算更高的 `level` 时作为 `append` 的副产品,调用栈内的四个 `f` 函数创建了四个底层数组^注6 ,但是没有影响到当前各自的切片。 +你可以注意到在每一个 `level` 内 `s` 的值没有被别的 `f` 的调用影响,尽管当计算更高的 `level` 时作为 `append` 的副产品,调用栈内的四个 `f` 函数创建了四个底层数组[^6] ,但是没有影响到当前各自的切片。 ### 扩展阅读 @@ -202,14 +202,12 @@ level: 0 slice: [0] * [Go Slices: usage and internals][5] (blog.golang.org) * [Arrays, slices (and strings): The mechanics of 'append'][6] (blog.golang.org) -### 注释 - -- 注1:这不是数组才有的特性,在 Go 语言里中 _一切_ 赋值都是复制过去的。 -- 注2:你也可以在对数组使用 `len` 函数,但是其结果本来就人尽皆知。 -- 注3:有时也叫做后台数组backing array,以及更不严谨的说法是后台切片。 -- 注4:Go 语言里我们倾向于说值类型以及指针类型,因为 C++ 的引用reference类型这个词产生误会。但在这里我认为调用数组作为引用类型是没有问题的。 -- 注5:如果你的结构体有[定义在其上的方法或者用于满足某个接口][7],那么你传入结构体指针的比率可以飙升到接近 100%。 --注 6:证明留做习题。 +[^1]: 这不是数组才有的特性,在 Go 语言里中 _一切_ 赋值都是复制过去的。 +[^2]: 你也可以在对数组使用 `len` 函数,但是其结果本来就人尽皆知。 +[^3]: 有时也叫做后台数组backing array,以及更不严谨的说法是后台切片。 +[^4]: Go 语言里我们倾向于说值类型以及指针类型,因为 C++ 的引用reference类型这个词产生误会。但在这里我认为调用数组作为引用类型是没有问题的。 +[^5]: 如果你的结构体有[定义在其上的方法或者用于满足某个接口][7],那么你传入结构体指针的比率可以飙升到接近 100%。 +[^6]: 证明留做习题。 ### 相关文章: From e6853d01a92c0b81cce7d6696599a2504310b8de Mon Sep 17 00:00:00 2001 From: "Xingyu.Wang" Date: Sun, 19 Aug 2018 22:15:36 +0800 Subject: [PATCH 3/3] PUB:20180712 Slices from the ground up.md @name1e5s @pityonline https://linux.cn/article-9933-1.html --- .../tech => published}/20180712 Slices from the ground up.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {translated/tech => published}/20180712 Slices from the ground up.md (100%) diff --git a/translated/tech/20180712 Slices from the ground up.md b/published/20180712 Slices from the ground up.md similarity index 100% rename from translated/tech/20180712 Slices from the ground up.md rename to published/20180712 Slices from the ground up.md