227 votes

Pile vs allocation de tas de structures en Aller, et comment ils se rapportent à la collecte des déchets

Je suis nouveau à l'Aller, et je suis en train de vivre un peu de congitive dissonance entre C-style basée sur la pile de programmation où les variables automatiques en direct sur la pile et de la mémoire allouée vie sur le tas et et Python de style basée sur la pile de programmation où la seule chose qui vit sur la pile sont des références/pointeurs vers des objets sur le tas.

Aussi loin que je peux dire, les deux fonctions suivantes, donner le même résultat:

func myFunction() (*MyStructType, error) {
    var chunk *MyStructType = new(HeaderChunk)

    ...

    return chunk, nil
}


func myFunction() (*MyStructType, error) {
    var chunk MyStructType

    ...

    return &chunk, nil
}

c'est à dire affecter une nouvelle structure et de le retourner.

Si j'avais écrit que, dans C, le premier aurait mis un objet sur le tas et la deuxième serait de le mettre sur la pile. La première serait de retourner un pointeur sur le tas, la seconde à retourner un pointeur sur la pile, ce qui aurait évaporé, le temps que la fonction était de retour, ce qui serait une Mauvaise Chose.

Si j'avais écrit en Python (ou de beaucoup d'autres langues modernes à l'exception de C#) exemple 2 n'aurait pas été possible.

Je reçois Aller à déchets collecte à la fois les valeurs, de sorte que les deux formes sont très bien.

Pour citer:

Notez que, contrairement au C, il est parfaitement OK pour retourner à l'adresse d'un variable locale; le stockage associé à la variable survit après le retour de fonction. En fait, prendre l'adresse d'un composite littéral alloue une nouvelle instance à chaque fois qu'il est évalué, de sorte que nous pouvez combiner ces deux dernières lignes.

http://golang.org/doc/effective_go.html#functions

Mais cela soulève quelques questions.

1 - Dans l'exemple 1, la structure est déclarée sur le tas. Qu'en est l'exemple 2? C'est que déclaré sur la pile de la même façon qu'en C ou faut-il aller sur le tas?

2 - Si l'exemple 2 est déclaré sur la pile, comment fait-il pour rester disponibles après la fonction retourne?

3 - Si l'exemple 2 est effectivement déclaré sur le tas, comment est-ce que les structures sont passés par valeur plutôt que par référence? Quel est le point de pointeurs dans ce cas?

226voto

Sonia Points 6077

Il est intéressant de noter que les paroles de "pile" et "tas" n'apparaissent pas dans la langue spec. Votre question est formulée avec "...est déclaré sur la pile", et "...a déclaré sur le tas", mais note que Aller syntaxe de déclaration ne dit rien sur la pile ou le tas.

Qui techniquement fait la réponse à toutes vos questions dépendant de l'implémentation. En réalité, bien sûr, il y a une pile (par goroutine!) et un tas et certaines choses aller sur la pile et certains sur le tas. Dans certains cas, le compilateur respecte les règles rigides (comme "new alloue toujours sur le tas") et dans d'autres, le compilateur ne "s'échapper d'analyse" pour décider si un objet peut vivre sur la pile ou si elle doit être alloué sur le tas.

Dans votre exemple 2, échapper à l'analyse d'afficher le pointeur vers la structure de fuir et de sorte que le compilateur aurait allouer de la struct. Je pense que l'actuelle mise en œuvre de Go suit une règle rigide, dans ce cas, cependant, qui est que si l'adresse est prise de toute partie d'une structure, la structure va sur le tas.

Pour la question 3, on risque de se confondre sur la terminologie. Tout de Go est passé par valeur, il n'y a pas de passage par référence. Ici vous retournent une valeur de type pointeur. Quel est le point de pointeurs? Considérons la suite de la modification de votre exemple:

type MyStructType struct{}

func myFunction1() (*MyStructType, error) {
    var chunk *MyStructType = new(MyStructType)
    // ...
    return chunk, nil
}

func myFunction2() (MyStructType, error) {
    var chunk MyStructType
    // ...
    return chunk, nil
}

type bigStruct struct {
    lots [1e6]float64
}

func myFunction3() (bigStruct, error) {
    var chunk bigStruct
    // ...
    return chunk, nil
}

J'ai modifié myFunction2 pour revenir à la structure plutôt que l'adresse de la structure. Comparer l'assemblée de sortie de myFunction1 et myFunction2 maintenant,

--- prog list "myFunction1" ---
0000 (s.go:5) TEXT    myFunction1+0(SB),$16-24
0001 (s.go:6) MOVQ    $type."".MyStructType+0(SB),(SP)
0002 (s.go:6) CALL    ,runtime.new+0(SB)
0003 (s.go:6) MOVQ    8(SP),AX
0004 (s.go:8) MOVQ    AX,.noname+0(FP)
0005 (s.go:8) MOVQ    $0,.noname+8(FP)
0006 (s.go:8) MOVQ    $0,.noname+16(FP)
0007 (s.go:8) RET     ,

--- prog list "myFunction2" ---
0008 (s.go:11) TEXT    myFunction2+0(SB),$0-16
0009 (s.go:12) LEAQ    chunk+0(SP),DI
0010 (s.go:12) MOVQ    $0,AX
0011 (s.go:14) LEAQ    .noname+0(FP),BX
0012 (s.go:14) LEAQ    chunk+0(SP),BX
0013 (s.go:14) MOVQ    $0,.noname+0(FP)
0014 (s.go:14) MOVQ    $0,.noname+8(FP)
0015 (s.go:14) RET     ,

Ne vous inquiétez pas que myFunction1 de sortie ici est différente de celle de peterSO (excellent) réponse. Nous sommes évidemment l'exécution de différents compilateurs. Sinon, de voir que je transforme myFunction2 de retour myStructType plutôt que *myStructType. L'appel à l'exécution.les nouvelles est allé, qui, dans certains cas, serait une bonne chose. Tenir sur bien, voici myFunction3,

--- prog list "myFunction3" ---
0016 (s.go:21) TEXT    myFunction3+0(SB),$8000000-8000016
0017 (s.go:22) LEAQ    chunk+-8000000(SP),DI
0018 (s.go:22) MOVQ    $0,AX
0019 (s.go:22) MOVQ    $1000000,CX
0020 (s.go:22) REP     ,
0021 (s.go:22) STOSQ   ,
0022 (s.go:24) LEAQ    chunk+-8000000(SP),SI
0023 (s.go:24) LEAQ    .noname+0(FP),DI
0024 (s.go:24) MOVQ    $1000000,CX
0025 (s.go:24) REP     ,
0026 (s.go:24) MOVSQ   ,
0027 (s.go:24) MOVQ    $0,.noname+8000000(FP)
0028 (s.go:24) MOVQ    $0,.noname+8000008(FP)
0029 (s.go:24) RET     ,

Toujours pas d'appel pour l'exécution.nouvelle, et oui, il fonctionne vraiment pour retourner un 8MB objet par valeur. Il fonctionne, mais vous ne voulez pas. Le point d'un pointeur ici serait d'éviter de pousser autour de 8 MO objets.

72voto

peterSO Points 25725
type MyStructType struct{}

func myFunction1() (*MyStructType, error) {
    var chunk *MyStructType = new(MyStructType)
    // ...
    return chunk, nil
}

func myFunction2() (*MyStructType, error) {
    var chunk MyStructType
    // ...
    return &chunk, nil
}

Dans les deux cas, les implémentations actuelles de Go serait d'allouer de la mémoire pour un struct de type MyStructType sur un segment de mémoire et retourne son adresse. Les fonctions sont équivalentes; le compilateur asm source est la même.

--- prog list "myFunction1" ---
0000 (temp.go:9) TEXT    myFunction1+0(SB),$8-12
0001 (temp.go:10) MOVL    $type."".MyStructType+0(SB),(SP)
0002 (temp.go:10) CALL    ,runtime.new+0(SB)
0003 (temp.go:10) MOVL    4(SP),BX
0004 (temp.go:12) MOVL    BX,.noname+0(FP)
0005 (temp.go:12) MOVL    $0,AX
0006 (temp.go:12) LEAL    .noname+4(FP),DI
0007 (temp.go:12) STOSL   ,
0008 (temp.go:12) STOSL   ,
0009 (temp.go:12) RET     ,

--- prog list "myFunction2" ---
0010 (temp.go:15) TEXT    myFunction2+0(SB),$8-12
0011 (temp.go:16) MOVL    $type."".MyStructType+0(SB),(SP)
0012 (temp.go:16) CALL    ,runtime.new+0(SB)
0013 (temp.go:16) MOVL    4(SP),BX
0014 (temp.go:18) MOVL    BX,.noname+0(FP)
0015 (temp.go:18) MOVL    $0,AX
0016 (temp.go:18) LEAL    .noname+4(FP),DI
0017 (temp.go:18) STOSL   ,
0018 (temp.go:18) STOSL   ,
0019 (temp.go:18) RET     ,

Appels

Dans un appel de fonction, la valeur de la fonction et les arguments sont évalués en l'ordre habituel. Après ils sont évalués, les paramètres de l'appel sont passés par valeur de la fonction et de la fonction appelée commence l'exécution. Le retour des paramètres de la fonction sont passés par valeur de retour à l'appel de la fonction lorsque la fonction retourne.

Toutes les fonctions et de revenir les paramètres sont passés par valeur. Le paramètre de retour de la valeur type *MyStructType est une adresse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X