webentwicklung-frage-antwort-db.com.de

Ein Array als Argument in Golang übergeben

Warum funktioniert das nicht?

package main

import "fmt"

type name struct {
    X string
}

func main() {
    var a [3]name
    a[0] = name{"Abbed"}
    a[1] = name{"Ahmad"}
    a[2] = name{"Ghassan"}

    nameReader(a)
} 

func nameReader(array []name) {
    for i := 0; i < len(array); i++ {
        fmt.Println(array[i].X)
    }
}

Error:

.\structtest.go:15: cannot use a (type [3]name) as type []name in function argument
29
user1721803

Sie haben Ihre Funktion so definiert, dass sie ein Slice als Argument akzeptiert, während Sie versuchen, ein Array im Aufruf an diese Funktion zu übergeben. Es gibt zwei Möglichkeiten, wie Sie dies angehen können:

  1. Erstellen Sie beim Aufrufen der Funktion ein Slice aus dem Array. Das Ändern des Anrufs wie folgt sollte ausreichen:

    nameReader(a[:])
    
  2. Ändern Sie die Funktionssignatur, um ein Array anstelle eines Slice aufzunehmen. Zum Beispiel:

    func nameReader(array [3]name) {
        ...
    }
    

    Nachteil dieser Lösung ist, dass die Funktion jetzt nur ein Array der Länge 3 akzeptieren kann und beim Aufruf eine Kopie des Arrays erstellt wird.

Weitere Informationen zu Arrays und Slices sowie zu häufigen Fallstricken bei deren Verwendung finden Sie hier: http://openmymind.net/The-Minimum-You-Need-To-Know-About-Arrays-And-Slices-In -Gehen/

39

Da @ james-henstridges Antwort bereits darauf abzielte, wie man es schaffen könnte, werde ich nicht wiederholen, was er gesagt hat, aber ich werde warum erklären, dass seine Antwort funktioniert.

In Go funktionieren Arrays etwas anders als in den meisten anderen Sprachen (ja, es gibt Arrays und Slices. Ich werde später auf Slices eingehen). In Go haben Arrays eine feste Größe, wie Sie sie in Ihrem Code verwenden ([3]int ist also ein anderer Typ als [4]int). Arrays sind außerdem Werte. Dies bedeutet, dass ich, wenn ich ein Array von einem Ort an einen anderen kopiere, tatsächlich alle Elemente des Arrays kopiere (statt wie in den meisten anderen Sprachen nur einen anderen Verweis auf dasselbe Array). Zum Beispiel:

a := [3]int{1, 2, 3} // Array literal
b := a               // Copy the contents of a into b
a[0] = 0
fmt.Println(a)       // Prints "[0 2 3]"
fmt.Println(b)       // Prints "[1 2 3]"

Wie Sie jedoch bemerkt haben, hat Go auch Slices. Slices ähneln Arrays, jedoch auf zwei verschiedene Arten. Erstens sind sie variable Länge (also []int ist der Typ eines Slice mit einer beliebigen Anzahl von Ganzzahlen). Zweitens sind Slices Referenzen. Dies bedeutet, dass beim Erstellen eines Slice ein Speicherplatz zugewiesen wird, um den Inhalt des Slice darzustellen, und die Slice-Variable selbst ist wirklich nur ein Zeiger auf diesen Speicher. Wenn ich dann dieses Stück kopiere, kopiere ich wirklich nur den Zeiger. Das heißt, wenn ich das Slice kopiere und dann einen der Werte ändere, ändere ich diesen Wert für alle. Zum Beispiel:

a := []int{1, 2, 3} // Slice literal
b := a              // a and b now point to the same memory
a[0] = 0
fmt.Println(a)      // Prints "[0 2 3]"
fmt.Println(b)      // Prints "[0 2 3]"

Implementierung

Wenn diese Erklärung ziemlich leicht verständlich war, könnten Sie auch neugierig sein, wie dies implementiert wird (wenn Sie Schwierigkeiten hätten, das zu verstehen, würde ich hier aufhören zu lesen, da die Details wahrscheinlich nur verwirrend sind).

Unter der Haube sind Go-Scheiben eigentlich Strukturen. Wie bereits erwähnt, haben sie einen Zeiger auf den zugewiesenen Speicher, aber auch auf andere Schlüsselkomponenten: Länge und Kapazität. Wenn es in Go-Begriffen beschrieben würde, würde es ungefähr so ​​aussehen:

type int-slice struct {
    data *int
    len  int
    cap  int
}

Die Länge ist die Länge des Slice und es ist dort, so dass Sie nach len(mySlice) fragen können, und Go kann außerdem prüfen, ob Sie nicht auf ein Element zugreifen, das sich nicht im Slice befindet. Die Kapazität ist jedoch etwas verwirrender. Lasst uns ein bisschen tiefer tauchen.

Wenn Sie zum ersten Mal ein Slice erstellen, geben Sie eine Anzahl von Elementen an, die das Slice sein soll. Wenn Sie beispielsweise make([]int, 3) aufrufen, erhalten Sie einen Schnitt von 3 Ints. Dies bedeutet, dass Speicherplatz für 3 Ints reserviert wird und Sie eine Struktur mit einem Zeiger auf die Daten, die Länge von 3 und die Kapazität von 3 zurückgeben.

In Go können Sie jedoch das tun, was Slicing heißt. Hier erstellen Sie im Grunde ein neues Slice aus einem alten Slice, das nur einen Teil des alten Slice darstellt. Sie verwenden die slc[a:b]-Syntax, um auf die Unterschicht von slc zu verweisen, die mit dem Index a beginnt und unmittelbar vor dem Index b endet. Also zum Beispiel:

a := [5]int{1, 2, 3, 4, 5}
b := a[1:4]
fmt.Println(b) // Prints "[2 3 4]"

Bei diesem Slicing-Vorgang wird unter der Haube eine Kopie der Struktur erstellt, die a entspricht, und der Zeiger wird auf den Wert 1 Integer vorwärts im Speicher (da der neue Slice bei Index 1 beginnt) und die Länge so bearbeitet, dass sie vorgeht 2 kürzer als zuvor (weil die alte Scheibe Länge 5 hatte, während die neue Scheibe Länge 3 hatte). Wie sieht das nun in Erinnerung aus? Nun, wenn wir uns die Integer-Zahlen vorstellen könnten, würde das ungefähr so ​​aussehen:

  begin     end  // a
  v         v
[ 1 2 3 4 5 ]
    ^     ^
    begin end    // b

Beachten Sie, wie es nach dem Ende von b noch einen int gibt? Nun, das ist die Kapazität. Solange die Erinnerung für uns verwendet wird, können wir genauso gut alles nutzen. Selbst wenn Sie nur ein Stück haben, dessen Länge klein ist, wird es sich daran erinnern, dass mehr Kapazität vorhanden ist, falls Sie es jemals zurück haben möchten. Also zum Beispiel:

a := []int{1, 2, 3}
b := a[0:1]
fmt.Println(b) // Prints "[1]"
b = b[0:3]
fmt.Println(b) // Prints "[1 2 3]"

Sehen Sie, wie wir b[0:3] dort am Ende erledigen? Die Länge von b ist zu diesem Zeitpunkt in Wirklichkeit less als 3, der einzige Grund, warum wir das können, ist, dass Go die Tatsache nachverfolgt hat, dass wir uns im zugrundeliegenden Speicher tatsächlich befinden bekam mehr Kapazität reserviert. Auf diese Weise, wenn wir etwas zurückfragen, kann es glücklich sein.

24
joshlf

Ein alternativer Ansatz

Man könnte eine Variadic-Funktion im a-Slice aufrufen, um die Liste der Namen als einzelne Argumente für die nameReader-Funktion einzugeben, z.

package main

import "fmt"

type name struct {
    X string
}

func main() {
    a := [3]name{{"Abbed"}, {"Ahmed"}, {"Ghassan"}}
    nameReader(a[:]...)
}

func nameReader(a ...name) {
    for _, n := range a {
        fmt.Println(n.X)
    }
}
6
XaxD

Anstatt ein Array mit einer Größe zu deklarieren, deklarieren Sie ein Slice. Weisen Sie dem Speicher Speicher zu und füllen Sie ihn aus. Sobald Sie das haben, ist der ursprüngliche Verweis immer noch auf das Slice, das an eine Funktion übergeben werden kann. Betrachten Sie dieses einfache Beispiel

package main

import "fmt"

func main() {
  // declare a slice
  var i []int

  i = make([]int, 2)
  i[0] = 3
  i[1] = 4

  // check the value - should be 3
  fmt.Printf("Val - %d\n", i[0])

  // call the function  
  a(i)

  // check the value again = should be 33
  fmt.Printf("Val - %d\n", i[0])  
}

func a(i []int) {
  // check the value - should be 3
  fmt.Printf("Val - %d\n", i[0])  

  // change the value
  i[0] = 33

  // check the value again = should be 33
  fmt.Printf("Val - %d\n", i[0])  
}

Wie Sie sehen, wurde das Array als Referenz übergeben und kann von der entsprechenden Funktion geändert werden.

Die Ausgabe sieht folgendermaßen aus: 

Val - 3
Val - 3
Val - 33
Val - 33

Das gesamte Programm finden Sie auch unter: http://play.golang.org/p/UBU56eWXhJ

2
lsu_guy

Übergeben von Arrays als Parameter.

Array-Werte werden als einzelne Einheit behandelt. Eine Array-Variable ist kein Zeiger auf einen Speicherplatz im Speicher, sondern stellt den gesamten "Block of Memory" dar, der die Array-Elemente enthält. Dies hat die Auswirkungen, dass eine neue Kopie eines Array-Werts erstellt wird, wenn die Array-Variable neu zugewiesen wird oder als Funktionsparameter übergeben werden .Learning Go-Programmierung, Von Vladimir Vivien

Dies kann unerwünschte Nebeneffekte auf den Speicherverbrauch eines Programms haben. Sie können dies mithilfe von "Zeigertypen" beheben, um auf Array-Werte zu verweisen. Zum Beispiel:

stattdessen das tun:

var numbers [1024*1024]int

du musst tun:

type numbers [1024*1024]int
var nums *numbers = new(numbers)

Erinnere dich daran: 

https://golang.org/pkg/builtin/#new

Die neue integrierte Funktion weist Speicher zu. Das erste Argument ist ein Typ, nicht ein Wert, und der zurückgegebene Wert ist ein Zeiger auf ein neu Nullwert dieses Typs zugewiesen.

Jetzt können Sie den Arrayzeiger ohne Nebeneffekt des Speicherverbrauchs an die Funktion übergeben und ihn nach Belieben verwenden.

nums[0] = 10
doSomething(nums)

func doSomething(nums *numbers){
  temp := nums[0]
  ...
}

Dabei ist zu beachten, dass der Array-Typ ein Low-Level-Speicherkonstrukt in Go ist und als Basis für Speicherprimitive verwendet wird, bei denen strikte Speicherzuordnungsanforderungen zur Minimierung des Speicherplatzbedarfs bestehen. In den Fällen, in denen Ihre Anforderungen auf die Leistung angewiesen sind, müssen Sie sich dafür entscheiden, mit Arrays (wie im vorherigen Beispiel) anstelle von Slices zu arbeiten.

0

Wir können nur Scheiben verwenden. Führen Sie den Code hier aus: http://play.golang.org/p/WtcOvlQm01

Denken Sie daran, dass [3]name ein Array ist. []name ist ein Slice.

package main

import "fmt"

type name struct {
    X string
}

func main() {
    a := []name{name{"Abbed"}, name{"Ahmad"}, name{"Ghassan"}}
    nameReader(a)
} 

func nameReader(array []name) {
    for i := 0; i < len(array); i++ {
        fmt.Println(array[i].X)
    }
}

Lesen Sie weiter: 50 Shades of Go: Fallen, Gotchas und häufige Fehler für neue Golang Devs

0
Vince Yuan