2015-04-29 3 views
-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 ожидает указатель на []byte, то я согласен, но в этом случае нет.

ответ

2

Все передается по значению (путем создания копии передаваемого значения).

Но поскольку кусочки в Go просто дескрипторы для смежного сегмента нижележащего массива, дескриптор будет скопирован, который будет ссылаться на тот же базовый массив, так что если вы измените содержимом среза, то тот же базовый массив модифицируется.

Если вы изменяете значение среза в функции, которое не отражается в вызывающем месте, потому что значение среза - это просто копия, и копия будет изменена (а не значение дескриптора исходного фрагмента).

Если передать указатель, значение указателя, также передаются по значению (значение указателя будет скопировано), но в этом случае, если вы измените указали значение, которое будет таким же, как на (копия указателя и исходного указателя указывает на тот же объект/значение).

Похожие статьи в блоге:

Go Slices: usage and internals

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

+1

Кусочек не является ссылочным типом. Это тип значения, который содержит ссылку на базовый массив. –

+0

@ Анонимно _reference_ Я не имею в виду то же, что _pointer_. Но отредактирован, чтобы сделать его однозначным. Благодарю. – icza

+0

@ Тип анонимного _Reference_ - это не то же самое, что тип _Pointer_. Пожалуйста, проверьте статью в блоге [Go maps in action] (http://blog.golang.org/go-maps-in-action): _ «Типы карт являются ** ссылочными ** типами, такими как указатели или ** фрагменты ** "_ – icza

1

Врезка заголовка в Go содержит в себе указатель на подстилающий массив.

Вы можете прочитать на официальном блоге: https://blog.golang.org/slices

Даже если заголовок секции передается по значению, заголовок содержит указатель на элементы массива, так как оригинальный заголовок секции и копия заголовка, переданного функции, описывают один и тот же массив. Поэтому, когда функция возвращается, измененные элементы можно увидеть через исходную переменную среза.

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/slices).
Но с точки зрения кода он ведет себя точно так же, как указатель C.

Смежные вопросы