Wartości
Często spotykam się z tym, że programiści utożsamiają zmienną z jej zawartością. A jest to gruba nieprawda: zmienna to tylko nazwa (identyfikator) dzięki której możemy się odwołać do konkretnej wartości (która jest trzymana gdzieś w pamięci). Na przykładach:
a := "wartość"
Zmienna a
wskazuje na wartość którą jest ciąg znaków "wartość"
. To że nie
ma zmiennej wskazującej na jakąś wartość nie oznacza, że nie mamy takiej wartości
zapisanej w pamięci, choć jest to mocna wskazówka dla garbage collectora, by
taka pamięć zwolnić. Zmienna określonego typu może przechowywać tylko wartości
które są "przypisywalne" do tego typu.
Sposoby tworzenia wartości
Są trzy podstawowe sposoby tworzenia wartości (a co za tym idzie alokowania pamięci)
- zastosowanie wbudowanej funkcji new()
- zastosowanie wbudowanej funkcji make()
- zastosowanie literału
Napisałem "podstawowe", a to dlatego, że gdy operujemy na slice
możemy "przez
przypadek" utworzyć całkiem nową wartość. Dokładniej zgłębimy temat w artykule
o wycinkach.
Wszystkie się od siebie różnią dlatego omówimy je osobno.
new()
To nietypowa funkcja dla Go, bo jako argument przyjmuje typ, a nie wartość, a co gorsza zwraca nową wartość ale innego typu niż jej przekazaliśmy :-)
A tak na poważnie: funkcja new
zajmuje się alokowaniem pamięci dla
wartości, której typ został przekazany w wywołaniu i zwraca wskaźnik do niej.
W kodzie wygląda to tak:
type point struct {
a int
b int
}
var p *point
p = new(point)
Zauważ, że zadeklarowaliśmy p jako wskaźnik do wartości typu point.
Funkcja new
inicjalizuje wartość na tak zwane zero typu, czyli jeśli typem
był struct
to wszystkie pola tej struktury zostaną wyzerowane; a jeśli typem
jest int to zwróci wskaźnik do nowo utworzonej wartości zero.
Właśnie ze względu na tę ostatnią cechę nie używamy new dla typów referencyjnych:
map, slice, chan, bo zerem dla typu referencyjnego jest nil
zmienna wskazująca
na nil
nie jest zbyt użyteczna :-)
make()
Dla typów referencyjnych tj. map, slice i chan została stworzona inna funkcja:
make
, której oprócz typu przekazujemy początkowe właściwości:
dla
slice
to będzie długość (len) i pojemność (cap), przy czym pojemność jest opcjonalna:sli := make([]int, len, cap) sli2 := make([]int, len)
dla
map
to wstępna ilość elementów (cap), opcjonalnam := make(map[int]string, cap) m1 := make(map[int]string)
dla
chan
możemy określić wielkość bufora elementów (len)ch := make(chan int, len)//kanał z buforem ch1 := make(chan int)//kanał bez bufora
make
zwraca wartość zgodną z typem przekazanym w wywołaniu.
Literały
Ze względu na objętość omówienie literałów wyłączyłem do osobnego artykułu