Golang bytes.Buffer-传递值问题

时间:2018-07-19 09:32:58

标签: go pass-by-value

下面的golang(go1.10.2)代码将产生意外的输出

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var b bytes.Buffer
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"\n", b.String())
    b = makeMeMad(b)
    fmt.Printf("FinalValue - \"%s\"\n", b.String())
}

func makeMeMad(b bytes.Buffer) bytes.Buffer {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"\n", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
    return b
}

输出

Before Calling - "aas-"
Write More - "aas-xcxxcx asdasdas dasdsd asdasdasdasd"
FinalValue - "aas-                                   "

我期望在输出的最后一行显示“ aas-xcxxcx asdasdas dasdsd asdasdasdasd”。谁能解释一下。

2 个答案:

答案 0 :(得分:1)

内部bytes.Buffer包含其他未导出字段bootstrap数组和buf切片。虽然缓冲区内容很小,但它指向内部数组以避免分配。当您将bytes.Buffer参数作为值传递时,函数将收到一个副本。 Slice是引用类型,因此此副本的slice继续指向原始缓冲区的数组。当您写入此副本的片时,实际上是写入原始副本的引导程序数组,副本的数组保持不变(在本例中为“ aas-”)。然后,您可以退回该副本并进行打印。但是,当您将其分配给包含原始变量的变量时,则会首先分配引导数组(“ aas-”),然后指向其上的buf切片。 Bootstrap数组为[64] byte,因此您可以在代码段中使用> 64的长字符串文字,当缓冲区分配buf slice时,see会按预期工作。 此外,here的简化示例试图说明为什么所有这些看起来如此有争议。

答案 1 :(得分:-1)

它在Golang FAQ部分中被提及为:

  

如果接口值包含指针* T,则可以调用方法   通过解引用指针来获得一个值,但如果是接口值   包含值T,没有有用的方法调用来获取   指针。

     

即使在编译器可以将值的地址用于   传递给该方法,如果该方法修改了值,则更改将   在呼叫者中迷路。例如,如果   bytes.Buffer使用值接收器而不是指针,此代码:

var buf bytes.Buffer
io.Copy(buf, os.Stdin)
  

会将标准输入复制到buf的副本中,而不是复制到buf本身

错误是因为您没有在makeMeMad函数内部传递缓冲区的地址。这就是为什么它没有覆盖main函数内部的原始缓冲区。 将地址传递到创建的缓冲区,以将字符串追加到现有缓冲区值。

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var b bytes.Buffer
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"\n", b.String())
    makeMeMad(&b)
    fmt.Printf("FinalValue - \"%s\"\n", b.String())
}

func makeMeMad(b *bytes.Buffer) {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"\n", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
}

Playground Example

或者您可以将返回的缓冲区值分配给新变量,您将获得更新的缓冲区值。

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var b bytes.Buffer
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"\n", b.String())
    ab := makeMeMad(b)
    fmt.Printf("FinalValue - \"%s\"\n", ab.String())
}

func makeMeMad(b bytes.Buffer) bytes.Buffer {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"\n", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
    return b
}

Go Playground上的工作代码

或者您可以创建一个全局缓冲区,以在任何函数写入缓冲区时更改缓冲区中的值。

package main

import (
    "bytes"
    "fmt"
)

var b bytes.Buffer

func main() {
    //Commenting the below line will fix the problem
    b.WriteString("aas-")
    fmt.Printf("Before Calling - \"%s\"\n", b.String())
    b := makeMeMad(b)
    fmt.Printf("FinalValue - \"%s\"\n", b.String())
}

func makeMeMad(b bytes.Buffer) bytes.Buffer {
    b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
    fmt.Printf("Write More - \"%s\"\n", b.String())

    /*
        //This will fix the problem
        var newBuffer bytes.Buffer
        newBuffer.WriteString(b.String())
        return newBuffer
    */
    return b
}

Playground Example