copy--Arrays,slices(and strings):The mechanics of 'append'

  • Arrays

Arrays are not often seen in Go programs because the size of an array is part of its type, which limits its expressive power.
The declaration
var buffer [256]byte
declares the variable buffer,which holds 256 bytes.
We can access its elements with the familiar indexing syntax,buffer[0], buffer[1] and so on through buffer[256].
There is a built-in function called len that returns the number of elements of an array or slice and also of a few other data types.
Arrays are a good representation of a transformation matrix for instace, but their most common purpose in Go is to hold storage for a slice.

  • The slice header

A slice is a data structure describing a contiguous section of an array stored separately from the slice variable itself. A slice describes a piece of an array.
Given our buffer array variable from the previous sevtion, we could create a slice that describes emements 100 through 150 by slicing the array:

var slice []byte = buffer[100:150]
var slice = buffer[100:150]
slice := buffer[100:150]

What exactly is the slice variable?It's not quite the full strory,but bow think of the slice being built like this behind the scenes:

type sliceHeader struct {
        Length         int
        ZerothElement  *byte
}
slice := sliceHeader {
         Length:          50,
         ZerothElement:   &buffer[100]
}

Despite what this snippet says that sliceHeader struct is not visible to the programmer, and the type of the element pointer depends on the type of the elements, but this gives the general idea of the mechanics.

  • Passing slices to functions

It's important to understand that even though a slice contains a pointer,it is itself a value.Under the covers,it is a struct value holding a pointer and a length.It's not a pointer to a struct.
When we called IndexRune in the previous example,it was passed a copy of the slice header.

func AddOneToEachElement(slice []byte) {
        for 1:= range slice {
            slice[i]++
        }
}
func main() {
        slice := buffer[10:20]
        for i := 0; i < len(slice); i++ {
              slice[i] = byte(i)
        }
        fmt.Println("before", slice)
        AddOneToEachElement(slice)
        fmt.Println("after",slice)
}

the result:

before [0 1 2 3 4 5 6 7 8 9]
after [1 2 3 4 5 6 7 8 9 10]

Even though the slice header is passed by value,the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array.Therefore, when function returns, the modified elements can be seen through the original slice variable.
The argument to the function really is a copy, as this example shows:

func SubtractOneFromLength(slice []byte) []byte {
        slice = slice[0 : len(slice)-1]
        return slice
}
func main() {
        fmt.Println("Before: len(slice) =", len(slice))
        newSlice := SubtractOneFromLength(slice)
        fmt.Println("After: len(slice) = ", len(slice))
        fmt.Println("After:len(newSlice) =", len(newSlice))
}

the result:

Before: len(slice) = 10
After: len(slice) =  10
After:len(newSlice) = 11

Here we see that the contents of a slice argument can be modified by a function, but its header cannot. The length stored in the slice variable is not modified by the call to the function, since the function is passed a copy of thr slice header, not the original. Thus if we want to write a function that modifies the header, we must return it as a result parameter, just as we have done here. The slice variable is unchanged but the returned value has the new length, which is then stored in newSlice.

  • Pointers to slices:Method recievers

Anthor way to have a function modify the slice header is to pass a pointer to it. Here's a variant of our previous example that does this:

type path []byte
func (p path) ToUpper() {
    for i, b := range p {
        if 'a' <= b && b <= 'z' {
            p[i] = b + 'A' - 'a'
        }
    }
}
func main() {
    pathName := path("/usr/bin/tso")
    pathName.ToUpper()
    fmt.Printf("%s\n", pathName)
}
  • Capacity

Besides the array pointer and length, the slice header also stores its capacity:

type sliceHeader struct {
        Length int
       Capacity int
       ZerothElement *byte
}

The Capacity field records how much space the underlying array actually has; it is the maximum value the Length can reach.

  • Make

We can't grow the slice beyond its capacity,but we can achieve an equivalent result by allocating a new array, copying the data over, and modifying the slice to describe the new array.
We could use the new built-in function to allocate a bigger array and then slice the result, but it is simpler to use the make built-in function instead.

slice := make([]int, 10, 15)
newSlice := make([]int, len(slice), 2*cap(slice))
for i := range slice {
     newSlice[i] = slice[i]
}
slice = newSlice
  • Copy

Go has a built-in function, copy, which copies the data from the right-hand argument to the left-hand argument.The number of elements it copies is the minimum of the lengths of the two slices. Copy returns an integer value, the number of elements it copied.

newSlice := make([]int, len(slice), 2*cap(slice))
copy(newSlice,slice)
  • Append: The built-in function

//Append appends the elements to the slice
//Efficient version
func append(slice []int, elements ...int) []int {
        n := len(slice)
        total := len(slice) + len(elements)
        if total > cap(slice) {
           newSize := total*3/2 + 1
           newSlice := make([]int, total, newSize)
           copy(newSlice, slice)
           slice := newSlice
        }
        slice = slice[:total]
        copy(slice[n:], elements)
        return slice
}

Go provides a built-in generic append function. It works the same as out int slice version, but for any slice type.

slice := []int{1,2,3}
slice2 := []int{55,56,57}
slice = append(slice, 4)
slice2 = append(slice, slice2...)
slice3 := append([]int(nil), slice...)
slice = append(slice, slice)
  • Nil

A nil slice is the zero value of the slice header:

sliceHeader{
        Length: 0,
        Capacity: 0,
        ZerothElement: nil,
}

or just sliceHeader{}.
The slice created by array[0:0].

  • Strings

Strings are just read-only slices of bytes with a bit of extra syntactic support from the language.Because they are read-only, there is no need for a capacity (you can't grow them), but otherwise for most purposes you can treat them just like read-only slices of bytes.
We can also take a normal slice of bytes and create a string from it with the simple conversion:
str := string(slice)
and go in reverse direction as well:
slice := []byte(str)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容