-2

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

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传递给方法。Read在该Read方法之后,b1包含文件中的前 10 个字母。

让我对上面的代码感到非常困惑的是,为什么b1在方法之后突然包含值Read

在 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方法需要一个[]byteslice 指针,那么我会同意,但在这种情况下不是。

4

3 回答 3

2

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

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

如果你在函数中修改了切片值本身,那在调用的地方并没有体现出来,因为切片值只是一个副本,副本会被修改(不是原来的切片描述符值)。

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

相关博客文章:

Go Slices:用法和内部结构

数组、切片(和字符串):“追加”的机制

于 2015-04-29T07:18:01.663 回答
1

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

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

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

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

于 2015-04-29T08:08:14.963 回答