Reader界面变化值

The Reader interface change value

我有一个关于 reader 接口的问题,定义如下:

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

我有以下使用 reader 接口的代码:

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 被定义为字节切片,并作为值参数传递给 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 切片的指针,那么我会同意,但在这种情况下不会。

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

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

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 the function returns, the modified elements can be seen through the original slice variable.

一切都按值传递(通过创建传递值的副本)。

但是由于 Go 中的切片只是底层数组的连续段的 描述符 ,描述符将被复制,它将引用相同的底层数组,所以如果你修改切片的内容,相同的底层数组被修改。

如果在函数中修改切片值本身,调用处并不会体现出来,因为切片值只是一个拷贝,拷贝会被修改(不是原来的切片描述符值)。

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

相关博客文章:

Go Slices: usage and internals

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

这与在 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 切片包含的信息不仅仅是指针:它是一个小结构,包含指针、长度和分配数组的最大容量(参见 link其他答案中提到:https://blog.golang.org/slices).
但从代码的角度来看,它的行为与 C 指针完全一样。