Reader接口更改值

时间:2015-04-29 07:13:03

标签: go slice reader

我对阅读器界面有疑问,定义如下:

type Reader interface {
    Read(p []byte) (n int, err error)
}

我有以下使用阅读器界面的代码:

package main

import (
    "fmt"
    "os"
)

// Reading files requires checking most calls for errors.
// This helper will streamline our error checks below.
func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {

    // You'll often want more control over how and what
    // parts of a file are read. For these tasks, start
    // by `Open`ing a file to obtain an `os.File` value.
    f, err := os.Open("configuration.ini")
    check(err)

    // Read some bytes from the beginning of the file.
    // Allow up to 5 to be read but also note how many
    // actually were read.
    b1 := make([]byte, 10)
    n1, err := f.Read(b1)
    check(err)
    fmt.Printf("%d bytes: %s\n", n1, string(b1))

    f.Close()

}

正如您可以看到上面的代码,b1被定义为字节切片,并作为value参数传递给Read方法。在Read方法之后,b1包含文件中的前10个字母。

对于我上面这段代码非常困惑的是,为什么b1Read方法之后突然包含值。

在Golang中,当我将值传递给方法时,它将作为值传递而不是作为引用传递。为了澄清,我在说什么,我做了一个示例应用程序:

package main


import (
    "fmt"
)

func passAsValue(p []byte) {
    c := []byte("Foo")
    p = c
}

func main() {

    b := make([]byte, 10)
    passAsValue(b)
    fmt.Println(string(b))
}

passAsValue函数之后,b不包含任何值,并且我在golang中所期望的,参数将作为值传递给函数或方法。

为什么然后,第一个代码片段可以更改传递的参数的内容?如果Read方法需要[]byte切片的指针,那么我会同意,但在这种情况下不是。

3 个答案:

答案 0 :(得分:2)

所有内容都按值传递(通过创建传递的值的副本)。

但是由于Go中的切片只是基础数组的连续段的描述符,因此将复制描述符,该描述符将引用相同的基础数组,因此如果您修改内容切片的,修改了相同的底层数组。

如果在函数中修改切片值本身,则不会在调用位置反映切片值,因为切片值只是一个副本,并且将修改副本(而不是原始切片描述符值)。

如果传递指针,指针的值也会按值传递(指针值将被复制),但在这种情况下,如果修改指向的值,那将是与在调用位置相同(指针的副本和原始指针指向同一个对象/值)。

相关博客文章:

Go Slices: usage and internals

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

答案 1 :(得分:1)

Go中的切片标头本身包含指向底层数组的指针。

您可以阅读官方博客文章:https://blog.golang.org/slices

  

即使切片标头按值传递,标头也包含指向数组元素的指针,因此原始切片标头和传递给函数的标头副本都描述了相同的数组。因此,当函数返回时,可以通过原始切片变量看到修改后的元素。

答案 2 :(得分:0)

与在C中传递指针的行为完全相同:

#include <stdio.h>
#include <stdlib.h>

// p is passed by value ; however, this function does not modify p,
// it modifies the values pointed by p.
void read(int* p) {
    int i;
    for( i=0; i<10; i++) {
        p[i] = i+1;
    }
}

// p is passed by value, so changing p itself has no effect out
// of the function's scope
void passAsValue(int*p) {
   int* c = (int*)malloc(3*sizeof(int));

   c[0] = 15; // 'F' in hex is 15 ...
   c[1] = 0;
   c[2] = 0;

   p = c;
}

int main() {
    int* p = (int*)malloc(10*sizeof(int));
    int i;
    for( i=0; i<10; i++) {
        p[i] = 0;
    }

    printf("             init : p[0] = %d\n", p[0]);

    read(p);
    printf("       after read : p[0] = %d\n", p[0]);

    passAsValue(p);
    printf("after passAsValue : p[0] = %d\n", p[0]);

    return 0;
}

输出:

//             init : p[0] = 0
//       after read : p[0] = 1
//after passAsValue : p[0] = 1 // <- not 15, the modification from
//                             //    within passAsValue is not persistent

(对于记录:此C程序泄漏int* c数组)

Go切片包含的信息多于指针:它是一个小结构,包含分配数组的指针,长度和最大容量(请参阅其他答案中提到的链接:{ {3}}) 但从代码的角度来看,它的行为与C指针完全相同。