Passer un tableau comme argument dans golang

Pourquoi cela ne fonctionne-t-il pas?

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)
    }
}

Erreur:

.structtest.go:15: cannot use a (type [3]name) as type []name in function argument
27
demandé sur James Henstridge 2014-02-12 09:52:59

6 réponses

Vous avez défini votre fonction pour accepter une tranche comme argument, alors que vous essayez de passer un tableau dans l'appel à cette fonction. Il y a deux façons de résoudre ce problème:

  1. Créez une tranche du tableau lors de l'appel de la fonction. Changer l'appel comme ceci devrait suffire:

    nameReader(a[:])
    
  2. Modifiez la signature de la fonction pour prendre un tableau au lieu d'une tranche. Par exemple:

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

    Inconvénients de cette solution sont que la fonction peut maintenant n'acceptez qu'un tableau de longueur 3, et une copie du tableau sera faite lors de son appel.

Vous pouvez trouver plus de détails sur les tableaux et les tranches, et les pièges courants lors de leur utilisation ici: http://openmymind.net/The-Minimum-You-Need-To-Know-About-Arrays-And-Slices-In-Go/

33
répondu James Henstridge 2018-05-10 14:41:46

Puisque la réponse de @ james-henstridge a déjà couvert comment vous pourriez le faire fonctionner, Je ne vais pas dupliquer ce qu'il a dit, Mais je vais expliquer pourquoi sa réponse fonctionne.

Dans Go, les tableaux fonctionnent un peu différemment que dans la plupart des autres langages (oui, il y a des tableaux et des tranches. Je discuterai des tranches plus tard). Dans Go, les tableaux sont de taille fixe, comme vous l'utilisez dans votre code (donc, [3]int est un type différent de [4]int). De plus, les tableaux sont valeurs . Ce que cela signifie est que si je copie un tableau d'un endroit à l'autre, je copie en fait tous les éléments du tableau (au lieu, comme dans la plupart des autres langues, de faire simplement une autre référence au même tableau). Par exemple:

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]"

Cependant, comme vous l'avez remarqué, Go a aussi des tranches. Les tranches sont similaires aux tableaux, sauf de deux manières clés. Tout d'abord, ils sont de longueur variable (donc []int est le type d'une tranche de n'importe quel nombre d'entiers). Deuxièmement, les tranches sont références. Ce que cela signifie est que lorsque je crée une tranche, un morceau de la mémoire est allouée pour représenter le contenu de la tranche, et la variable slice elle-même n'est vraiment qu'un pointeur vers cette mémoire. Ensuite, quand je copie cette tranche, Je ne fais que copier le pointeur. Cela signifie que si je copie la tranche et change ensuite l'une des valeurs, Je change cette valeur pour tout le monde. Par exemple:

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]"

La mise en Œuvre

Si l'explication est assez facilement compréhensible, alors vous pourriez aussi être curieux de savoir comment c'est implémenté (si vous aviez du mal à comprendre cela, j'arrêterais de lire ici parce que les détails seront probablement confus).

Sous le capot, les tranches Go sont en fait des structures. Ils ont un pointeur sur la mémoire allouée, comme je l'ai mentionné, mais ils ont aussi d'autres composants clés: La longueur et la capacité. Si elle était décrite en termes de Go, cela ressemblerait à ceci:

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

La longueur est La longueur de la tranche, et c'est là que vous pouvez demander de l' len(mySlice), et aussi pour ce Go peut vérifier pour vous assurer que vous n'ACCÉDEZ PAS à un élément qui n'est pas réellement dans la tranche. La capacité, cependant, est un peu plus confus. Alors plongeons un peu plus loin.

Lorsque vous créez une tranche, vous donnez un certain nombre d'éléments que vous voulez que la tranche soit. Par exemple, appeler make([]int, 3) vous donnerait une tranche de 3 ints. Ce que cela fait est d'allouer de l'espace en mémoire pour 3 ints, puis de vous redonner une structure avec un pointeur vers les données, la longueur de 3 et la capacité de 3.

Cependant, en Go, vous pouvez faire ce qu'on appelle découpage. C'est typiquement l'endroit où vous créer une nouvelle tranche d'une vieille tranche qui ne représente qu'une partie de l'ancienne tranche. Vous utilisez slc[a:b] syntaxe pour voir la sous-tranche de slc commençant à l'index a et se terminant juste avant d'index b. Ainsi, par exemple:

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

Ce que cette opération de découpage fait sous le capot est de faire une copie de la structure qui correspond à a, et d'éditer le pointeur sur le point 1 integer forward en mémoire (parce que la nouvelle tranche commence à l'index 1), et éditez la longueur pour être 2 plus courte qu'avant (parce que l'ancienne tranche avait la longueur 5, tandis que la nouvelle a la longueur 3). Donc à quoi cela ressemble dans la mémoire maintenant? Eh bien, si nous pouvions visualiser les entiers disposés, cela ressemblerait à ceci:

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

Remarquez comment il y a encore un int après la fin de b? Eh bien, c'est la capacité. Vous voyez, tant que la mémoire va rester pour nous d'utiliser, nous pourrions ainsi être en mesure d'utiliser tout ça. Donc, même si vous n'avez qu'une tranche dont la longueur est petite, il se souviendra qu'il y a plus de capacité au cas où vous le voudriez. Ainsi, par exemple:

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]"

Vous voyez comment on fait b[0:3] à la fin? La longueur de b est en fait inférieure à à 3 à ce stade, donc la seule raison pour laquelle nous sommes capables de le faire est que Go a gardé une trace du fait que, dans la mémoire sous-jacente, nous avons en fait plus de capacité réservée. De cette façon, lorsque nous demandons pour certains de retour, il peut heureusement obliger.

21
répondu joshlf 2014-02-12 08:43:28

Une approche alternative

On pourrait invoquer une fonction variadique sur la tranche a pour entrer la liste des noms en tant qu'arguments individuels à la fonction nameReader, par exemple:

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)
    }
}
4
répondu XaxD 2018-01-29 08:34:37

Au lieu de déclarer un tableau de taille, déclarez une tranche. Allouez de la mémoire et remplissez-la. Une fois que vous avez cela, la référence d'origine est toujours à la tranche, qui peut être transmise à une fonction. Considérons cet exemple simple

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])  
}

Comme vous pouvez le voir, le tableau a été passé (comme référence) et peut être modifié par la fonction correspondante.

La sortie ressemble à ceci:

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

L'ensemble du programme peut également être trouvé à: http://play.golang.org/p/UBU56eWXhJ

1
répondu lsu_guy 2016-01-23 07:07:26

, Nous pouvons simplement utiliser des tranches. Exécutez le code ici: http://play.golang.org/p/WtcOvlQm01

Besoin de se rappeler [3]name est un tableau. []name est une tranche.

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)
    }
}

Pour en savoir plus: 50 Shades of Go: pièges, pièges et erreurs courantes pour les nouveaux développeurs de Golang

0
répondu Vince Yuan 2015-08-10 15:55:37

Passer des tableaux en tant que paramètres.

Les valeurs des tableaux sont traitées comme une seule unité. Une variable de tableau n'est pas un pointeur vers un emplacement en mémoire, mais représente plutôt l'ensemble du "Bloc de Mémoire" contenant les éléments du tableau. Cela a les implications de la création d'une nouvelle copie d'une valeur de tableau lorsque la variable de tableau est réaffectée ou transmise en tant que paramètre de fonction. Apprendre La Programmation Go, Par Vladimir Vivien

Cela pourrait avoir des effets secondaires indésirables sur la mémoire consommation pour un programme. Vous pouvez résoudre ce problème en utilisant "types de pointeurs" pour référencer les valeurs du tableau. Par exemple:

Faites plutôt ceci:

var numbers [1024*1024]int

Vous devez faire:

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

Rappelez-vous que:

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

La nouvelle fonction intégrée alloue de la mémoire. Le premier argument est un type, pas une valeur, et la valeur renvoyée est un pointeur vers valeur zéro allouée de ce type.

Maintenant, vous pouvez passer le pointeur de tableau à la fonction sans l'effet secondaire de la consommation de mémoire et l'utiliser comme vous le souhaitez.

nums[0] = 10
doSomething(nums)

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

Une chose à garder à l'esprit est que le type de tableau est une construction de stockage de bas niveau dans Go et est utilisé comme base pour les primitives de stockage, où il existe des exigences strictes d'allocation de mémoire pour minimiser la consommation d'espace. Pour les cas où votre exigence repose sur les performances, vous devez choisir de travailler avec des tableaux (comme l'exemple précédent) au lieu de tranches.

0
répondu Wilfredo Villegas 2017-01-11 15:37:44