一个对slice进行操作的问题

89 views
Skip to first unread message

Aruforce

unread,
May 6, 2019, 5:51:31 AM5/6/19
to Golang-China
package main

import (
    "fmt"
)

func main() {
    var slice []int = []int{0, 1, 2, 3, 4}
    fmt.Println(slice)
    testSlice(slice)
    fmt.Println(slice)
}
func testSlice1(slice1 []int) {
    slice1[0] = 100
    _ = append(slice1, 5, 6, 7)
}
func testSlice2(slice1 *[]int) {
    (*slice1)[0] = 100
    _ = append(*slice1, 5, 6, 7)
}

1. 我想实现的操作是:一个slice 进行append后 main方法可以感知到这个操作, 要求不改变方法签名
2. testSlice1 或者testSlice2 都不行,对slice的操作不应该是映射到底层的数组上么? 我对数组进行了append 为什么 main方法感知不到?
3. 是不是我理解有错误?

Ric Y

unread,
May 6, 2019, 5:59:43 AM5/6/19
to golang...@googlegroups.com
append 函数是返回一个新的对象,append返回的silence,是一个新的silence,和你创建的silence是不同的对象。

你直接把append返回的对象忽略了,怎么还可能在main函数里接收到呢


Aruforce <xm952...@gmail.com> 于2019年5月6日周一 下午5:51写道:
--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
---
您收到此邮件是因为您订阅了Google网上论坛上的“Golang-China”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china...@googlegroups.com
要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/golang-china/894e7ecd-88e0-4b2d-b02d-841e8f363ec3%40googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

Hyacinthus

unread,
May 6, 2019, 6:00:11 AM5/6/19
to Golang-China
package main

import (
    "fmt"
)

func main() {
    var slice []int = []int{0, 1, 2, 3, 4}
    fmt.Println(slice)
    testSlice(&slice)
    fmt.Println(slice)
}
func testSlice(slice1 *[]int) {
    (*slice1)[0] = 100
    *slice1 = append(*slice1, 5, 6, 7)
}


但是一般不这样干啊

你用全局变量或者返回新的slice都比较符合日常习惯。

from Muninn


Aruforce <xm952...@gmail.com> 于2019年5月6日周一 下午5:51写道:
package main

--

Sasasu

unread,
May 6, 2019, 6:01:43 AM5/6/19
to golang...@googlegroups.com
append 总是 cow,返回值是新地址而不是报错碼。
如果内存不足会发生 panic。

Ric Y

unread,
May 6, 2019, 6:06:20 AM5/6/19
to golang...@googlegroups.com
像你这种需求,用struct来实现吧,比如:

package main

import (
"fmt"
)

type myArray struct {
s []int
}

func (a *myArray) append(n ...int) {
a.s = append(a.s, n...)
}

func main() {
var arry myArray
arry.append(1, 2, 3)
fmt.Println(arry.s)
arry.append(4, 5, 6)
fmt.Println(arry.s)
}

输出:

[1 2 3]
[1 2 3 4 5 6]
 

Ric Y <youzhe...@gmail.com> 于2019年5月6日周一 下午5:59写道:

youzhe...@gmail.com

unread,
May 6, 2019, 6:12:21 AM5/6/19
to Golang-China
你这个silence里要装多少数据啊,居然还能把内存撑爆?
如果要处理大量数据用silence效率不是很低吗?为啥不用链表结构之类的?
而且既然涉及大量数据,你必然要做内存管理啊,数据量大到阈值之后,有其他逻辑判断处理。

在 2019年5月6日星期一 UTC+8下午6:01:43,Sasasu写道:
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china+unsubscribe@googlegroups.com

--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
---
您收到此邮件是因为您订阅了Google网上论坛上的“Golang-China”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china+unsubscribe@googlegroups.com
Message has been deleted

Aruforce

unread,
May 6, 2019, 6:58:21 AM5/6/19
to Golang-China
这样的写法,是把main方法里面的slice 存储的指针指向了 append返回的slice?我大概明白了...

那么下面这样的写法怎么报错了?

func testSlice2(slice1 *[]int) {
    (*slice1)[0] = 100
   // *slice1 = append(*slice1, 4)
    slice1 = &(append(*slice1, 4)) //这样写应该是和上面一个意思吧?编译器居然说不对。。。
}


在 2019年5月6日星期一 UTC+8下午6:00:11,Muninn写道:
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china+unsubscribe@googlegroups.com

Hyacinthus

unread,
May 6, 2019, 12:42:19 PM5/6/19
to Golang-China
当然不是一个意思,golang不能改指针。 另外你也许不知道golang的函数参数永远是值传递。

Aruforce <xm952...@gmail.com> 于 2019年5月6日周一 下午6:58写道:
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china...@googlegroups.com

--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
---
您收到此邮件是因为您订阅了Google网上论坛上的“Golang-China”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china...@googlegroups.com
要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/golang-china/19e45db4-6ffd-4e81-9672-c4ac6bbcaa92%40googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

Aruforce

unread,
May 6, 2019, 9:38:16 PM5/6/19
to Golang-China
func testSlice2(slice1 *[]int) {
    *slice1 = append(*slice1, 4)//slice1 存储的指针指向了新的地址
    slice1 = &(append(*slice1, 4))//slice1 存储的指针变成了新的值,这个新值指向了和append返回的slice,应该可以达到同样的效果?
//和上面的区别在于main里面的slice变量存储的值进行了更新,意思说不能对指针进行修改? 但是上面的不也是对指针进行了更新?还说slice1存储的并不是一个指针 而某种指针的包装类似于Java的引用这样的东西?指针不可以修改但是引用可以?
}

在 2019年5月7日星期二 UTC+8上午12:42:19,Muninn写道:
当然不是一个意思,golang不能改指针。 另外你也许不知道golang的函数参数永远是值传递。

要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china+unsubscribe@googlegroups.com

--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
---
您收到此邮件是因为您订阅了Google网上论坛上的“Golang-China”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china+unsubscribe@googlegroups.com

Hyacinthus

unread,
May 6, 2019, 10:11:14 PM5/6/19
to Golang-China
你要是老套别的语言,你永远也搞不明白。
最多套用 ANSI C 想象一下。

首先,slice 在main里边没有指针,它是切片本身。(不要考虑切片内部实现)
在调用函数的时候,创造了一个 slice的指针,这个指针又被复制成 slice1 传入了函数内部。指针复制了之后指向的还是同一片内存地址。
函数内部的操作是,把append产生的结果,写入slice1指向的地址。  slice1没有任何变化。

这更像 C++ 的引用。

但是你看,引用在不同语言里边也不一样,你纠结的话会越来越混淆。

所以除非你精通5种以上的语言,可以考虑用类比的思维去学习golang。

如果你只会一两种,那你只要一类比就掉坑里。千万忘掉之前学的其他语言就对了。

你这样类比,后边再一想面向对象和设计模式,golang就彻底学不会了。

from Muninn


Aruforce <xm952...@gmail.com> 于2019年5月7日周二 上午9:38写道:
当然不是一个意思,golang不能改指针。 另外你也许不知道golang的函数参数永远是值传递。

要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china...@googlegroups.com

--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
---
您收到此邮件是因为您订阅了Google网上论坛上的“Golang-China”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china...@googlegroups.com

--
--
官网: http://golang-china.org/
IRC: irc.freenode.net #golang-china
@golangchina
---
您收到此邮件是因为您订阅了Google网上论坛上的“Golang-China”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到golang-china...@googlegroups.com
要在网络上查看此讨论,请访问https://groups.google.com/d/msgid/golang-china/d0eed264-bc75-4b44-a30b-7320beb91710%40googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

Sasasu

unread,
May 7, 2019, 12:57:42 AM5/7/19
to golang...@googlegroups.com
slice 在不切片的情况下和 c++ 的 vector 一样,如果切片的话和 c++ 的 string_view(或者是未来的array_view) 一样。
slice 内部有一个指针和两个 uint(分别表示已用容量和可用容量)

问题在于 go 的 append 函数签名和实现。

append 接受一个 slice 的拷贝(拷贝一个指针和两个 uint)返回一个 slice 的拷贝。

当可用容量不够时 append 会拷贝原来 slice 内指针所指的东西到一个新的指针里,然后返回一个新的 slice (的拷贝)。

当容量足够时 append 会更新原来的 slice 并返回原来 slice 的拷贝。


func testSlice2(slice1 *[]int) {
   // 此时创建一个指针,这个指针是外面 &xxxx 的拷贝,名为 slice1
    *slice1 = append(*slice1, 4)  // slice1 指针指向的位置变成了新的slice
    slice1 = &(append(*slice1, 4)) // slice1 存储的指针变成了新的slice地址,这个新地址指向了和append返回的slice(可能是同一个),但因为 slice1 是外面 &xxx 的拷贝,所以不会影响到外面
}


这时候对着 slice1 再append会有更诡异的事情发生,前几个能append有几个就不能append进去了。

所以 testSlice1 是达不到目地的,go使用传拷贝还是传指针区分是不是 const (但是你可以随便 append 几个,有时候能进去有时候进不去,但外面都读不到)。

testSlice2 需要每次 append 都对 slice1 解引用。


Thanks & Regards,
Sasasu


Hyacinthus <hyaci...@gmail.com> 于2019年5月7日周二 上午10:11写道:

Sasasu

unread,
May 7, 2019, 1:27:37 AM5/7/19
to golang...@googlegroups.com
但实际上因为 abi 的关系 append 大概率不会拷贝任何参数...

上古时期 append 是不是为了展示 abi 而发明的呢...

Thanks & Regards,
Sasasu




Sasasu <lizhaol...@gmail.com> 于2019年5月7日周二 下午12:57写道:

Sasasu

unread,
May 7, 2019, 3:48:19 AM5/7/19
to golang...@googlegroups.com
好吧,我搞错了。

append的设计和 abi 没关系。

Thanks & Regards,
Sasasu




Sasasu <lizhaol...@gmail.com> 于2019年5月7日周二 下午1:27写道:
Reply all
Reply to author
Forward
0 new messages