459 lines
21 KiB
Markdown
459 lines
21 KiB
Markdown
# 2.2 Grundlagen von Go
|
||
|
||
In diesem Abschnit werden wir lernen, wie man Konstanten und Variablen mit grundlegenden Datentypen definiert, sowie ein paar weitere Fähigkeiten aus der Go-Programmierung.
|
||
|
||
## Variablen definieren
|
||
|
||
Es gibt syntaktisch viele Wege, eine Variable in Go zu definieren.
|
||
|
||
Das Schlüsselwort `var` ist die simpelste Form, eine Variable zu erstellen. Bedenke, dass in Go der Datentyp **hinter** dem Variablennamen steht.
|
||
|
||
// Definiere eine Variable mit dem Namen “variableName” vom Typ "type"
|
||
var variableName type
|
||
|
||
Definiere mehrere Variablen.
|
||
|
||
// Definiere drei Variablen vom Typ "type"
|
||
var vname1, vname2, vname3 type
|
||
|
||
Definiere eine Variable mit einem Startwert.
|
||
|
||
// Definiere eine Variable mit dem Namen “variableName” vom Typ "type" und dem Wert "value"
|
||
var variableName type = value
|
||
|
||
Definiere mehrere Variablen mit einem Startwert.
|
||
|
||
/*
|
||
Definiere drei Variablen vom Typ "type" und gib ihnen drei Startwerte.
|
||
vname1 ist gleich v1, vname2 ist gleich v2, vname3 ist gleich v3
|
||
*/
|
||
var vname1, vname2, vname3 type = v1, v2, v3
|
||
|
||
Findest Du es nicht auch ein wenig mühsehlig, Variablen auf diesem Weg zu definieren? Keine Sorge, denn das Team hinter Go hat auch so gedacht. Daher kannst Du Variablen Startwerte geben, ohne explizit den Datentyp zu definieren. Der Code würde dann so aussehen:
|
||
|
||
/*
|
||
Definiere drei Variablen vom Typ "type" und gib ihnen drei Ausgangswerte.
|
||
vname1 ist gleich v1, vname2 ist gleich v2, vname3 ist gleich v3
|
||
*/
|
||
var vname1, vname2, vname3 = v1, v2, v3
|
||
|
||
Schön, ich weiß das dies immer noch nicht einfach genug für Dich ist. Mal schauen, wie wir das lösen können.
|
||
|
||
/*
|
||
Definiere drei Variablen ohne den Typ "type", ohne das Schlüsselwort "var" und gib ihnen Startwerte.
|
||
vname1 ist gleich v1,vname2 ist gleich v2,vname3 ist gleich v3
|
||
*/
|
||
vname1, vname2, vname3 := v1, v2, v3
|
||
|
||
|
||
So sieht es schon viel besser aus. Nutze `:=`, um `var` und `type` zu ersetzen. Es handelt sich hierbei um eine Kurzschreibweise. Aber warte, es gibt einen kleinen Haken: diese Form der Definition kann nur innerhalb von Fuktionen genutzt werden. Der Compiler wird sonst eine Fehlermeldung ausgeben, wenn Du es trotzdem außerhalb der `{}` einer Funktion versuchst. Daher nutzen wir meist das Schlüsselwort `var` um globale Variablen zu definieren oder die Funktion `var()`.
|
||
|
||
`_` (Unterstrich) ist ein besonderer Variablenname. Jeder übergebene Wert wird ignoriert. Übergeben wir zum Beispiel `35` an `b`, so verwerfen wir `34`. ( ***Dieses Beispiel soll nur die Funktionsweise aufzeigen. Es mag, wie in diesem Fall, nutzlos erscheinen, aber wir werden es oft gebrauchen, wenn wir Rückgabewerte von Funktionen erhalten.*** )
|
||
|
||
_, b := 34, 35
|
||
|
||
Wenn Du Variablen in Deinem Programm definierst, aber keine Verwendung finden, wird der Compiler eine Fehlermeldung ausgeben. Versuche den unten stehenden Code zu kompilieren und schaue, was passiert.
|
||
|
||
package main
|
||
|
||
func main() {
|
||
var i int
|
||
}
|
||
|
||
## Konstanten
|
||
|
||
Konstanten sind Werte, die während der Kompilierung festgelegt werden und während der Laufzeit nicht veränderbar sind. In Go kannst Du Konstanten als Wert Nummern, Booleans oder Strings geben.
|
||
|
||
Definiere eine Konstante wie folgt.
|
||
|
||
const constantName = value
|
||
// Du kannst auch den Datentyp hinzufügen, wenn nötig
|
||
const Pi float32 = 3.1415926
|
||
|
||
Mehr Beispiele.
|
||
|
||
const Pi = 3.1415926
|
||
const i = 10000
|
||
const MaxThread = 10
|
||
const prefix = "astaxie_"
|
||
|
||
## Grundlegende Datentypen
|
||
|
||
### Boolean
|
||
|
||
In Go nutzen wir `bool`, um Booleans (Wahrheitswerte) auszudrücken, die entweder den Zustand `true` oder `false` annehmen können, wobei `false` der Standardwert ist. ( ***Du kannst übrigens Nummern zu Booleans und umgekehrt konvertieren!*** )
|
||
|
||
// Beispielcode
|
||
var isActive bool // Globale Variable
|
||
var enabled, disabled = true, false // Der Datentyp wird ausgelassen
|
||
func test() {
|
||
var available bool // Lokale Variable
|
||
valid := false // Kurzschreibweise einer Definition
|
||
available = true // Eine Variable deklarieren
|
||
}
|
||
|
||
### Numerische Datentypen
|
||
|
||
Der Datentyp Integer (ganze Zahlen) kann sowohl den positiven, als auch den negativen Zahlenraum umfassen, was durch ein Vorzeichen kenntlich gemacht wird. Go besitzt `int` und `uint`, welche den selben Wertebereich haben. Dessen Größe hängt aber vom Betriebssystem ab. Es werden 32-Bit unter 32-Bit Betriebssystemen verwendet und 64-Bit unter 64-Bit Betriebssystemen. Go umfasst außerdem Datentypen mit einer spezifischen Länge: `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32` und `uint64`. Bedenke, dass `rune` ein Alias für `int32` ist und `byte` dem `uint8` gleicht.
|
||
|
||
Eine wichtige Sache, die Du wissen solltest, ist, dass Du verschiedene Datentypen nicht vermischen kannst, da es sonst zu Fehlern bei der Kompilierung kommt.
|
||
|
||
var a int8
|
||
|
||
var b int32
|
||
|
||
c := a + b
|
||
|
||
|
||
Obwohl int32 einen größeren Wertebereich als int8 abdeckt, und beide vom Typ `int` sind, kannst Du sie nicht miteinander kombinieren. ( ***c wird hier der Typ `int` zugewiesen*** )
|
||
|
||
Floats (Gleitkommazahlen) haben entweder den Datentyp `float32` oder `float64`, aber es gibt keinen Datentyp namens `float`. `float64` wird standardmäßig verwendet, sollte die Kurzschreibweise für eine Variablendekleration genutzt werden.
|
||
|
||
War das schon alles? Nein! Go unterstützt auch komplexe Zahlen. `complex128` (bestehend aus 64-Bit für den realen Anteil und weiteren 64-Bit für den imaginären Teil) ist der Standarddatentyp. Solltest Du einen kleineren Wertebereich benötigen, kannst `complex64` als Datentyp verwenden (mit 32-Bit für den realen, und nochmals 32-Bit für den imaginären Teil). Die Schreibweise lautet `RE+IMi`, wo `RE` für den realen Teil steht, und `IM` den Imaginären Part repräsentiert. Das `i` am Ende ist die imaginäre Zahl. Hier ist ein Beispiel für eine komplexe Zahl.
|
||
|
||
var c complex64 = 5+5i
|
||
// Ausgabe: (5+5i)
|
||
fmt.Printf("Der Wert ist: %v", c)
|
||
|
||
### Strings
|
||
|
||
Wir sprachen vorhin darüber, das Go eine native UTF-8 Unterstützung mit sich bringt. Strings (Zeichenketten) werden durch Anführungszeichen `""` gekennzeichet, oder durch Backticks (rückwärts geneigtes Hochkomma) ``` `` ```.
|
||
|
||
// Beispielcode
|
||
var frenchHello string // Grundlegende Schreibweise zur Definition eines Strings
|
||
var emptyString string = "" // Definiert einen leeren String
|
||
func test() {
|
||
no, yes, maybe := "no", "yes", "maybe" // Kurzschreibweise
|
||
japaneseHello := "Ohaiou"
|
||
frenchHello = "Bonjour" // Grundlegende Deklaration
|
||
}
|
||
|
||
Es ist unmöglich, die Zeichen eines Strings anhand ihres Index zu verändern. Du wirst eine Fehlermeldung erhalten, solltest Du dies ausprobieren.
|
||
|
||
var s string = "Hallo"
|
||
s[0] = 'c'
|
||
|
||
Aber was ist, wenn ich wirklich nur ein Zeichen in einem String ändern möchte? Probieren wir es mit mit diesem Codebeispiel.
|
||
|
||
s := "hello"
|
||
c := []byte(s) // Konvertiere den String zum Typ []byte
|
||
c[0] = 'c'
|
||
s2 := string(c) // Wandle den Wert in eine String zurück
|
||
fmt.Printf("%s\n", s2)
|
||
|
||
|
||
Nutze den `+` Operator, um zwei Strings zu verknüpfen.
|
||
|
||
s := "Hallo"
|
||
m := " Welt"
|
||
a := s + m
|
||
fmt.Printf("%s\n", a)
|
||
|
||
|
||
oder auch
|
||
|
||
s := "Hallo"
|
||
s = "c" + s[1:] // Du kannst die Werte mit Hilfe des Index nicht verändern, aber sie abfragen
|
||
fmt.Printf("%s\n", s)
|
||
|
||
|
||
Was ist, wenn ein String über mehrere Zeilen verlaufen soll?
|
||
|
||
m := `Hallo
|
||
Welt`
|
||
|
||
`\`` wird die Zeichen in einem String escapen (d.h. mit `\` dessen Ausführung verhindern).
|
||
|
||
### Fehlermeldungen
|
||
|
||
Go besitzt mit `error` einen eigenen Datentyp, um mit Fehlermeldungen umzugehen. Zudem gibt es auch ein Paket mit dem Namen `errors`, um weitere Möglichkeiten bereitzustellen, Fehlermeldungen zu verarbeiten.
|
||
|
||
err := errors.New("emit macho dwarf: elf header corrupted")
|
||
if err != nil {
|
||
fmt.Print(err)
|
||
}
|
||
|
||
### Grundeliegende Datenstrukturen
|
||
|
||
Die folgende Grafik enstammt dem Artikel [Datenstrukturen in Go](http://research.swtch.com/godata) (auf englisch) aus [Russ Coxs Blog](http://research.swtch.com/). Wie Du sehen kannst, nutzt Go Abschnitte des Arbeitsspeichers, um Daten zu speichern.
|
||
|
||

|
||
|
||
Abbildung 2.1 Gos grundlegende Datenstrukturen
|
||
|
||
## Einige Fähigkeiten
|
||
|
||
### Gruppierte Definition
|
||
|
||
Wenn Du mehrere Konstanten und Variablen deklarieren oder Pakete importieren möchtest, kannst Du dies auch gruppiert durchführen.
|
||
|
||
Übliche Vorgehensweise.
|
||
|
||
import "fmt"
|
||
import "os"
|
||
|
||
const i = 100
|
||
const pi = 3.1415
|
||
const prefix = "Go_"
|
||
|
||
var i int
|
||
var pi float32
|
||
var prefix string
|
||
|
||
Gruppierter Ansatz.
|
||
|
||
import(
|
||
"fmt"
|
||
"os"
|
||
)
|
||
|
||
const(
|
||
i = 100
|
||
pi = 3.1415
|
||
prefix = "Go_"
|
||
)
|
||
|
||
var(
|
||
i int
|
||
pi float32
|
||
prefix string
|
||
)
|
||
|
||
Wird innerhalb von `constant()` einer Konstanten das Schlüsselwort `iota` als Wert zugewiesen, hat sie den Wert `0`. Werden den folgenden Konstanten keine expliziten Werte zugewiesen, wird der letzte zugeweise Wert von `iota` um 1 erhöht und der folgenden Konstante zugewiesen. Dieses Verhalten beleuchten wir im folgenden Absatz.
|
||
|
||
### Aufzählen mit iota
|
||
|
||
Go besitzt das Schlüselwort `iota`, um eine Aufzählung zu starten. Der Startwert ist `0` und wird jeweils um `1` erhöht.
|
||
|
||
const(
|
||
x = iota // x == 0
|
||
y = iota // y == 1
|
||
z = iota // z == 2
|
||
w // Folgt dem Namen der Konstante keine Deklaration, so wird die zuletzt erfolgte verwendet. w = iota wird somit implizit auf iota gesetzt. Daher gilt w == 3. Folglich kannst Du bei x und y "= iota" einfach auslassen.
|
||
)
|
||
|
||
const v = iota // Sobald iota erneut auf `const` trifft, wird erneut mit `0` gestartet, also gilt v = 0.
|
||
|
||
const (
|
||
e, f, g = iota, iota, iota // e, f und g haben den selben Wert 0, da sie in der selben Zeile stehen.
|
||
)
|
||
|
||
### Einige Regeln
|
||
|
||
Der Grund, warum Go so prägnant ist, liegt im vorhersehbaren Verhalten der Programmiersprache.
|
||
|
||
- Jede Variable, die mit einem großgeschriebenen Buchstaben anfängt, kann exportiert werden. Andernfalls ist sie privat.
|
||
- Die selbe Regel gilt auch für Funktionen und Konstanten. Schlüsselwörter wie `public` oder `private` existieren in Go nicht.
|
||
|
||
## Array, Slice, Map
|
||
|
||
### Array
|
||
|
||
Ein `array` ist eine Aneinanderreihung von Daten, die wie folgt definiert wird:
|
||
|
||
var arr [n]Datentyp
|
||
|
||
wobei in `[n]Datentyp` das `n` die Länge des Arrays angibt. `Datentyp` ist selbsterklärend der Datentyp der Elemente (bzw. der aneinandergereihten Daten). Wie in anderen Programmiersprachen nutzen wir `[]`, um Daten im Array zu speichern oder um sie auszulesen.
|
||
|
||
var arr [10]int // Ein Array vom Typ [10]int
|
||
arr[0] = 42 // Der erste Index des Arrays ist 0
|
||
arr[1] = 13 // Einem Element wird ein Wert zugewiesen
|
||
fmt.Printf("Das erste Element ist %d\n", arr[0]) // Beim Auslesen des Wertes wird 42 zurückgegeben
|
||
fmt.Printf("Das letzte Element ist %d\n", arr[9]) // Es gibt den Standardwert des 10. Elements zurück, was in diesem Fall 0 ist.
|
||
|
||
Da die Länge ein Teil des Array-Typs ist, sind `[3]int` und `[4]int` verschieden, sodass wir die Länge eines Arrays nicht ändern können. Nutzt Du Arrays als Argument in einer Funktion, dann wird eine Kopie des Arrays übergeben, statt einem Zeiger (bzw. ein Verweis) auf das Original. Möchtest Du stattdessen den Zeiger übergeben, dann musst Du einen `slice` verwenden. Darauf gehen wir aber später nochmals ein.
|
||
|
||
Es ist möglich, `:=` zu nutzen, um ein Array zu deklarieren.
|
||
|
||
a := [3]int{1, 2, 3} // Deklariere ein Array vom Typ int mit drei Elementen
|
||
b := [10]int{1, 2, 3} // Deklariere ein int-Array mit zehn Elementen, bei dem die ersten Drei einen Wert zugewiesen bekommen. Der Rest erhält den Standardwert 0.
|
||
c := [...]int{4, 5, 6} // Nutze `…` statt der Längenangabe. Go wird die Länge dann selbst bestimmen.
|
||
|
||
Vielleicht möchtest Du auch Arrays als Element in einem Array nutzen. Schauen wir mal, wie das geht.
|
||
|
||
// Deklariere ein zweidimensionales Array mit zwei Elementen, welche jeweils vier Elemente besitzen.
|
||
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
|
||
|
||
|
||
// Die Dekleration kann auch ein wenig kompakter geschrieben werden.
|
||
{% raw %} easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}} {% endraw %}
|
||
|
||
Arrays sind grundlegende Datenstrukturen in Go.
|
||
|
||

|
||
|
||
Abbildung 2.2 Die Zuordnung in einem mehrdimensionalen Array
|
||
|
||
### Slice
|
||
|
||
In vielen Situationen ist ein Array als Datentyp keine gute Wahl, wenn wir während der Deklaration dessen Länge noch nicht wissen. Daher brauchen wir so etwas wie ein "dynamisches Array" mit einer variabler Länge. Diese werden in Go `slice` genannt.
|
||
|
||
`slice` ist nicht wirklich ein `dynamisches Array`. Es ist vielmehr ein Zeiger auf ein darunterliegendes `array` mit einer ähnlichen Deklaration, dass aber keine Länge benötigt.
|
||
|
||
// Man deklariert ein Slice wie ein Array, lässt jedoch die Länge weg.
|
||
var fslice []int
|
||
|
||
Nun deklarieren wir ein `slice` und vergeben Startwerte.
|
||
|
||
slice := []byte {'a', 'b', 'c', 'd'}
|
||
|
||
`slice` kann existierende Slices oder Arrays verändern. `slice` nutzt `array[i:j]` zum "Herrausschneiden" von Elementen. `i` gibt den Index des Startpunkts an und kopiert alle Elemente bis zum Index `j`. Beachte, dass `array[j]` nicht in dem Ausschnitt enthalten ist, da das Slice eine Länge von `j-i` hat.
|
||
|
||
// Deklariere ein Array mit der Länge 10 von vom Typ byte
|
||
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
|
||
|
||
// Erstelle zwei Slices vom Typ []byte
|
||
var a, b []byte
|
||
|
||
// 'a' verweist auf die Elemente zwischen Index 3 und 5 im Array ar.
|
||
a = ar[2:5]
|
||
// 'a' besitzt nun die Elemente ar[2],ar[3] und ar[4]
|
||
|
||
// 'b' ist ein weiterer Ausschnitt (Slice) vom Array ar
|
||
b = ar[3:5]
|
||
// 'b' besitzt nun die Elemente ar[3] und ar[4]
|
||
|
||
Beachte die Unterscheide bei der Deklaration von `slice` und `array`. Wir nutzen `[…]`, um Go die Länge automatisch herausfinden zu lassen, aber nutzen `[]` lediglich zur Deklaration von Slices.
|
||
|
||
Ihre zugrundeliegenden Datentypen.
|
||
|
||

|
||
|
||
Abbildung 2.3 Der Zusammenhang zwischen Slices und Arrays
|
||
|
||
Slices haben bestimmte Anwendungsgebiete.
|
||
|
||
- Ein `slice` beginnt mit dem Index 0, `ar[:n]` gleicht `ar[0:n]`.
|
||
- Der zweite Index gibt die Länge vom Slice an, wenn er ausgelassen wird. `ar[n:]` gleicht `ar[n:len(ar)]`.
|
||
- Du kannst auch `ar[:]` nutzen, um einen gesamtes Array zu kopieren, wie in den ersten beiden Punkten erklärt.
|
||
|
||
Mehr Beispiele zu `slice`
|
||
|
||
// Deklariere ein Array
|
||
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
|
||
// Deklariere zwei Slices
|
||
var aSlice, bSlice []byte
|
||
|
||
// Einige gewöhnliche Anwendungsfälle
|
||
aSlice = array[:3] // ist das Gleiche wie aSlice = array[0:3]. aSlice hat die Elemente a,b,c
|
||
aSlice = array[5:] // ist das Gleiche wie aSlice = array[5:10]. aSlice hat die Elemente f,g,h,i,j
|
||
aSlice = array[:] // ist das Gleiche wie aSlice = array[0:10]. aSlice beinhaltet alle Elemente
|
||
|
||
// Ein Slice vom Slice
|
||
aSlice = array[3:7] // aSlice hat die Elemente d,e,f,g,Anfang=4,Kapazität=7
|
||
bSlice = aSlice[1:3] // bSlice beinhaltet aSlice[1], aSlice[2], also hat es die Elemente e,f
|
||
bSlice = aSlice[:3] // bSlice beinhaltet aSlice[0], aSlice[1], aSlice[2], also hat es die Elemente d,e,f
|
||
bSlice = aSlice[0:5] // Der Slice ist nun länger, sodass bSlice d,e,f,g,h beinhaltet
|
||
bSlice = aSlice[:] // bSlice hat nun die gleiche Elemente wie aSlice, also d,e,f,g
|
||
|
||
`slice` ist ein Datentyp mit einem Zeiger, sodass Änderungen am Slice auch andere Variablen verändern, die wiederum selbt auf den Slice verweisen. Wie im oberigen Fall von `aSlice` und `bSlice`: veränderst Du den Wert eines Elements in `aSlice`, wird sich dieser auch im `bSlice` ändern.
|
||
|
||
`slice` ist ähnlich wie ein Struct und besteht aus drei Komponenten:
|
||
|
||
- Ein Zeiger, der auf den Beginn des `slice` bzw. zugrundeliegenden Array verweist.
|
||
- Die Länge definiert den Ausschnitt des zugrundeliegenden Arrays.
|
||
- Kapazität definiert die max. Größe des zugrundeliegenden Arrays.
|
||
|
||
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
|
||
Slice_a := Array_a[2:5]
|
||
|
||
Die zugrundeliegenden Datenstrukturen vom vorherigen Code sehen wie folgt aus:
|
||
|
||

|
||
|
||
Abbildung 2.4 Arrayelemente eines Slice
|
||
|
||
Es existieren eingebaute Funktionen, um mit Slices zu arbeiten.
|
||
|
||
- `len` gibt die Länge eines `slice` bzw. Ausschnitts zurück.
|
||
- `cap` gibt die max. Länge (Kapazität) eines `slice` zurück.
|
||
- `append` erweitert den `slice` um ein oder mehrere Elemente, und gibt einen `slice` zurück.
|
||
- `copy` erstellt eine Kopie eines Slices, und gibt die Anzahl der kopierten Elemente zurück.
|
||
|
||
Achtung: `append` wird das Array, aus das `slice` verweist verändern, sowie andere Slices, die auf das selbe Array verweisen. Sollte de Kapazität des zugrundeliegenden Arrays nicht ausreichen (`(cap-len) == 0`), dann gibt `append` ein neues Array zurück. Dies hat aber keine Auswirkungen auf andere Slices, die auf das alte Array verwiesen.
|
||
|
||
### Map
|
||
|
||
`map` verhält sich wie ein Dictionary in Python. Nutze das Schema `map[SchlüsselTyp]WerteTyp`, um es zu deklarieren.
|
||
|
||
Schauen wir uns ein wenig Code an. Die 'set'- und 'get'-Werte in `map` sind der von `slice` sehr ähnlich. In einem Slice kann der Datentyp des Index nur ein `int` sein. In einer `map` kann es sich jedoch um einen `int`, `string` oder um jeden anderen Datentyp handeln. Du kannst auch `==` und `!=` verwenden, um Werte mit einander zu vergleichen.
|
||
|
||
// Nutze 'string' als Datentyp für den Schlüssel, 'int' als Datentyp für den Wert und `make` zum Erstellen.
|
||
var numbers map[string] int
|
||
// Ein alternativer Weg zum Deklarieren
|
||
nummern := make(map[string]int)
|
||
nummern["eins"] = 1 // Weise ein Wert durch einen Schlüssel zu
|
||
nummern["zehn"] = 10
|
||
nummern["drei"] = 3
|
||
|
||
fmt.Println("Die dritte Nummer lautet: ", nummern["drei"]) // Lese den Wert aus
|
||
// Ausgabe: Die dritte Nummer lautet: 3
|
||
|
||
Einige Anmerkungen zur Nutzung von maps.
|
||
|
||
- `map` ist ungeordnet. Jedesmal, wenn Du eine `map` ausgeben willst, erhälst Du ein anderes Ergebnis. Dadurch ist es unmöglich, Werte über den `index` abzufragen. Nutze dafür den entsprechenden `Schlüssel`.
|
||
- `map` hat keine feste Länge. Dieser Datentyp ist wie `slice` lediglich ein Verweis.
|
||
- `len` funktioniert bei `map` auch. Es werden die Anzahl der `Schlüssel` zurückgegeben.
|
||
- Es ist ziemlich einfach, der Wert in einer `map` zu ändern. Nutze `nummern["eins"]=11`, um den `Schlüssel` eins den Wert `11` zuzuweisen.
|
||
|
||
Du kannst das Schema `Schlüssel:Wert` nutzen, um eine `map` mit Startwerten zu erstellen. `map` hat auch eingebaute Funktionen, um die Existenz eines `key` zu überprüfen.
|
||
|
||
Benutze `delete`, um ein Element in `map` zu löschen.
|
||
|
||
// Erstelle eine map
|
||
bewertung := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
|
||
// Map hat zwei Rückgabewerte. Der zweite Rückgabewert 'ok' wird, wenn der Schlüssel nicht existiert,aus false gesetzt. Andernfalls wird true zurückgegeben.
|
||
csharpBewertung, ok := bewertung["C#"]
|
||
if ok {
|
||
fmt.Println("C# ist in der map hat die Bewertung ", csharpBewertung)
|
||
} else {
|
||
fmt.Println("Es konnte keine Bewertung für C# in der map gefunden werden.")
|
||
}
|
||
|
||
delete(bewertung, "C") // Lösche das Element mit dem Schlüssel "c"
|
||
|
||
Wie ich bereits sagte, ist `map` lediglich ein Verweis. Verweisen zwei `map`s auf die gleiche, zugrundeliegende Datenstruktur, wird eine Änderung Auswirkungen auf beide `map`s haben.
|
||
|
||
m := make(map[string]string)
|
||
m["Hallo"] = "Bonjour"
|
||
m1 := m
|
||
m1["Hallo"] = "Salut" // Nun ist der Wert von m["hello"] Salut
|
||
|
||
### make, new
|
||
|
||
`make` reserviert Speicher für die eingebauten Datentypen, wie `map`, `slice`, und `channel`, wo hingegen `new` für selbstdefinierte Datentype (durch `type` definiert) Speicher zuweist.
|
||
|
||
`new(T)` ordnet dem Datentyp `T` Speicherplatz zu und initialisiert diesen mit den Standardwerten des jeweiligen Datentyps (z.B. `false` für einen `bool`). Anschließend wird die Adresse des Speicherortes in des Datentyps `*T` zurückgegeben. Nach der Definition von Go ist dies ein Zeiger, welcher auf die Standardwerte der Initilisierung von `T` verweist.
|
||
|
||
`new` gibt Zeiger als Wert zurück.
|
||
|
||
Die eingebaute Funktion `make(T, args)` hat einen anderen Zweck als `new(T)`. `make` kann für `slice`, `map`, und `channel` genutzt werden und gibt den Datentyp `T` mit seinem definierten Startwert zurück. Der Grund liegt darin, dass das ein Objekt vom zugrundeliegenden Datentyp erst erstellt wird, bevor auf diesen verwiesen werden kann. Dies ist bei diesen drei Datentypen der Fall. Zum Beispiel beinhaltet `slice` einen Zeiger, der auf das zugrundeliegende Array, die Länge und die Kapazität verweist. Vor der Initialisierung der Daten beinhaltet `slice` jedoch nur `nil`. Daher vergibt `make` den Datentypen `slice`, `map` und `channel` geeignetere Werte.
|
||
|
||
`make` gibt dabei die angesprochenen Standardwerte der entsprechenden Datentypen zurück (z.B. `false` für einen `bool`).
|
||
|
||
Die folgende Grafik verdeutlicht den Unterschied zwischen `new` und `make`.
|
||
|
||

|
||
|
||
Abbildung 2.5 Wie make und new Datenstrukturen Speicherplatz zuweisen
|
||
|
||
Standardwerte besitzen einen Wert. Dies sind die gebräuchlichsten Anwendungsfälle. Hier eine kleine Liste von Standardwerten.
|
||
|
||
int 0
|
||
int8 0
|
||
int32 0
|
||
int64 0
|
||
uint 0x0
|
||
rune 0 // rune ist ein Alias für int32
|
||
byte 0x0 // byte ist ein Alias für uint8
|
||
float32 0 // Die Größe beträgt 4 Byte
|
||
float64 0 // Die Größe beträgt 8 Byte
|
||
bool false
|
||
string ""
|
||
|
||
## Links
|
||
|
||
- [Inhaltsverzeichnis](preface.md)
|
||
- Vorheriger Abschnitt: ["Hallo Go"](02.1.md)
|
||
- Nächster Abschnitt: [Kontrollstrukturen und Funktionen](02.3.md)
|