1 Wprowadzenie do typów danych w języku Go
W języku Go typy danych stanowią podstawę programowania, określając formę danych, które mogą przechowywać zmienne. Podstawowe typy danych dostarczane przez język Go są głównie podzielone na następujące kategorie:
Typ danych | Opis | Zużycie pamięci |
---|---|---|
bool | Typ logiczny, używany do przechowywania wartości true lub false | 1 bajt |
int, uint | Liczby całkowite ze znakiem i bez znaku, domyślny rozmiar zależy od platformy systemowej | 4 lub 8 bajtów |
int8, uint8 | 8-bitowe liczby całkowite ze znakiem i bez znaku | 1 bajt |
int16, uint16 | 16-bitowe liczby całkowite ze znakiem i bez znaku | 2 bajty |
int32, uint32 | 32-bitowe liczby całkowite ze znakiem i bez znaku | 4 bajty |
int64, uint64 | 64-bitowe liczby całkowite ze znakiem i bez znaku | 8 bajtów |
float32 | Liczba zmiennoprzecinkowa 32-bitowa | 4 bajty |
float64 | Liczba zmiennoprzecinkowa 64-bitowa | 8 bajtów |
complex64 | Liczba zespolona z 32-bitową częścią rzeczywistą i urojoną | 8 bajtów |
complex128 | Liczba zespolona z 64-bitową częścią rzeczywistą i urojoną | 16 bajtów |
byte | Podobne do uint8 | 1 bajt |
rune | Podobne do int32, reprezentuje punkt kodowy Unicode | 4 bajty |
string | Typ łańcuchowy | Zależy od długości łańcucha |
error | Interfejs błędu, używany do zwracania informacji o błędzie | Bez ustalonego rozmiaru |
Te typy można wybierać w zależności od różnych potrzeb, takich jak obliczenia numeryczne, przetwarzanie tekstów czy sterowanie logiczne.
2 Typy danych całkowitoliczbowych
2.1 Przegląd typów całkowitoliczbowych
W języku Go istnieje wiele wbudowanych typów całkowitoliczbowych, sklasyfikowanych następująco:
- Liczby całkowite ze znakiem:
int8
,int16
,int32
(lubrune
),int64
orazint
- Liczby całkowite bez znaku:
uint8
(lubbyte
),uint16
,uint32
,uint64
orazuint
Rozmiary int
i uint
wynoszą 4 bajty w systemach 32-bitowych i 8 bajtów w systemach 64-bitowych. Zakresy wartości typów danych całkowitoliczbowych są przedstawione w poniższej tabeli:
Typ | Zakres wartości |
---|---|
int8 | -128 do 127 |
uint8 | 0 do 255 |
int16 | -32768 do 32767 |
uint16 | 0 do 65535 |
int32 | -2147483648 do 2147483647 |
uint32 | 0 do 4294967295 |
int64 | -9223372036854775808 do 9223372036854775807 |
uint64 | 0 do 18446744073709551615 |
2.2 Użycie zmiennych całkowitoliczbowych
Podstawowa składnia deklarowania zmiennej całkowitoliczbowej jest następująca:
var nazwa_zmiennej typ_danych = wartosc_poczatkowa
Przykładowy kod:
package main
import "fmt"
func main() {
var a int = 10 // Zmienna całkowita ze znakiem
var b uint = 20 // Zmienna całkowita bez znaku
var c int8 = -128 // Najmniejsza wartość int8
fmt.Println(a, b, c)
}
2.3 Operacje na liczbach całkowitych
Język Go obsługuje standardowe operatory arytmetyczne, takie jak dodawanie (+
), odejmowanie (-
), mnożenie (*
), dzielenie (/
), i reszta z dzielenia (%
), a także operatory bitowe, takie jak bitowe AND (&
), OR (|
), XOR (^
), przesunięcie w lewo (<<
), i przesunięcie w prawo (>>
).
package main
import "fmt"
func main() {
x := 10
y := 3
// Operacje arytmetyczne
fmt.Println(x + y) // Dodawanie
fmt.Println(x - y) // Odejmowanie
fmt.Println(x * y) // Mnożenie
fmt.Println(x / y) // Dzielenie
fmt.Println(x % y) // Reszta z dzielenia
// Operacje bitowe
fmt.Println(x & y) // Bitowe AND
fmt.Println(x | y) // Bitowe OR
fmt.Println(x ^ y) // Bitowe XOR
fmt.Println(x << 1) // Przesunięcie w lewo o 1
fmt.Println(x >> 1) // Przesunięcie w prawo o 1
}
3 Typy danych zmiennoprzecinkowe
3.1 Przegląd typów danych zmiennoprzecinkowych
W języku Go typy zmiennoprzecinkowe obejmują float32
i float64
, odpowiadające 32-bitowym i 64-bitowym danym zmiennoprzecinkowym. Ogólnie zaleca się używanie float64
, ponieważ zapewnia on większy zakres i precyzję.
-
float32
ma około 23 istotne bity, co daje około 7 miejsc po przecinku. -
float64
ma około 52 istotne bity, co daje około 16 miejsc po przecinku.
3.2 Korzystanie z zmiennych zmiennoprzecinkowych
Zmienne zmiennoprzecinkowe można deklarować poprzez bezpośrednie podanie literałów lub używając słowa kluczowego var
:
package main
import "fmt"
func main() {
var f1 float32 = 3.14 // Wyraźne określenie typu float32
f2 := 3.14 // Automatycznie wywnioskowany jako typ float64
fmt.Println(f1, f2)
}
3.3 Arytmetyka zmiennoprzecinkowa i problemy
Arytmetyka zmiennoprzecinkowa może prowadzić do utraty precyzji. Wykonywanie operacji o bardzo dużej precyzji jest powszechnym problemem, zwłaszcza przy odejmowaniu dwóch bardzo bliskich liczb.
package main
import "fmt"
func main() {
f1 := .1
f2 := .2
f3 := f1 + f2
fmt.Println(f3) // Wynik może nie być oczekiwanym .3 z powodu problemów z precyzją
// Naprawa problemów z precyzją za pomocą sformatowanego wyjścia
fmt.Printf("%.1f\n", f3) // Wynik poprawiony do .3
}
4 Typ danych boolowski
4.1 Przegląd typu danych boolowskiego
Boolowski jest najprostszym typem danych i może przyjmować tylko dwie wartości: true
(prawda) i false
(fałsz). Odgrywa bardzo ważną rolę w instrukcjach warunkowych i strukturach kontrolnych pętli.
4.2 Korzystanie z zmiennych boolowskich
Deklaracja i stosowanie zmiennych boolowskich:
package main
import "fmt"
func main() {
var sukces bool = true
var niepowodzenie bool = false
fmt.Println("Operacja udana:", sukces)
fmt.Println("Operacja zakończona niepowodzeniem:", niepowodzenie)
}
Wartości boolowskie są często używane w instrukcjach warunkowych:
package main
import "fmt"
func main() {
a := 10
b := 20
fmt.Println("a == b:", a == b) // false
fmt.Println("a < b:", a < b) // true
}
5 Typ danych łańcuchów znaków
5.1 Przegląd łańcuchów znaków
Łańcuch znaków jest zbiorem znaków. W języku Go łańcuchy znaków są niemutowalne. Każdy łańcuch składa się z dwóch części: wskaźnika do podstawowej tablicy bajtów i długości. Łańcuchy znaków mogą zawierać dowolne dane, włącznie z bajtami.
5.2 Korzystanie z zmiennych łańcuchów znaków
Zmienne łańcuchów znaków są zazwyczaj deklarowane za pomocą podwójnych cudzysłowów "
do tworzenia, ale można też używać odwrotnych apostrofów ` do tworzenia wieloliniowych ciągów znaków:
package main
import "fmt"
func main() {
var s1 string = "witaj"
s2 := "świecie"
s3 := `To jest
ciąg znaków
wieloliniowy`
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
}
Po utworzeniu ciągu znaków jego zawartość nie może być zmieniona. Następująca operacja jest nielegalna i spowoduje błąd kompilacji:
s := "witaj"
s[] = 'W` // Błąd kompilacji: zawartość ciągu znaków jest niemutowalna
5.3 Operacje na łańcuchach znaków
Łańcuchy znaków są bardzo powszechne i ważne w programowaniu. Język Go zapewnia bogaty zestaw wbudowanych funkcji do manipulacji łańcuchami znaków. Oto kilka często używanych operacji.
5.3.1 Konkatenacja łańcuchów znaków
W języku Go można użyć operatora plus (+
) do konkatenacji łańcuchów znaków, co jest najprostszą metodą. Dodatkowo, podczas częstego łączenia wielu łańcuchów znaków zaleca się użycie strings.Builder
dla lepszej wydajności.
package main
import (
"fmt"
"strings"
)
func main() {
// Łączenie łańcuchów znaków za pomocą +
hello := "Cześć, "
world := "Świecie!"
wynik := hello + world
fmt.Println(wynik) // Wynik: Cześć, Świecie!
// Łączenie łańcuchów znaków za pomocą strings.Builder
var sb strings.Builder
sb.WriteString("Cześć, ")
sb.WriteString("Świecie!")
fmt.Println(sb.String()) // Wynik: Cześć, Świecie!
}
5.3.2 Podział ciągów znaków
Podział ciągów znaków można wykonać przy użyciu funkcji strings.Split
, która dzieli ciąg znaków na fragmenty na podstawie określonego separatora.
package main
import (
"fmt"
"strings"
)
func main() {
// Zdefiniowanie ciągu znaków
zdanie := "Go to język programowania typu open source"
// Podział ciągu znaków po spacji
slowa := strings.Split(zdanie, " ")
for _, slowo := range slowa {
fmt.Printf("%s\n", slowo)
}
// Wynik:
// Go
// to
// język
// programowania
// typu
// open
// source
}
5.3.3 Dostęp do indeksu
W Go, ciąg znaków to niezmienne ciąg bajtów. Możesz użyć indeksowania do uzyskania dostępu do określonych bajtów w ciągu znaków. Jednak należy zauważyć, że ponieważ ciąg znaków może zawierać znaki wielobajtowe (takie jak znaki kodowane w UTF-8), bezpośrednie indeksowanie może nie zwrócić oczekiwanego pojedynczego znaku.
package main
import "fmt"
func main() {
s := "Cześć, 世界"
for i := 0; i < len(s); i++ {
fmt.Printf("%d: %x\n", i, s[i])
}
// Uwaga: zostanie wyświetlona szesnastkowa reprezentacja bajtów, a nie znaków
}
Aby iterować przez ciąg znaków po znakach, można użyć pętli range
.
package main
import "fmt"
func main() {
s := "Cześć, 世界"
for index, wartoscRune := range s {
fmt.Printf("%d: %U '%c'\n", index, wartoscRune, wartoscRune)
}
// Wynik: indeks, kodowanie Unicode i sam znak dla każdego znaku
}
5.3.4 Pobieranie długości
Funkcja len
może uzyskać długość ciągu znaków, czyli długość podstawowego ciągu bajtów. Dla ciągów UTF-8, jeśli chcesz uzyskać liczbę znaków (runes), powinieneś użyć funkcji utf8.RuneCountInString
. To pozwala poprawnie obsługiwać znaki wielobajtowe.
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "Cześć, 世界"
fmt.Println("Długość bajtów:", len(s)) // Wyświetl długość bajtów
fmt.Println("Długość znaków:", utf8.RuneCountInString(s)) // Wyświetl długość znaków
}
Z powyższego przykładu wynika, że język Go dostarcza bogate funkcje biblioteki do operacji na ciągach znaków, co ułatwia wykonanie różnych zadań związanych z przetwarzaniem ciągów znaków. Podczas pisania kodu ważne jest zwrócenie uwagi na różnicę między bajtami a znakami, zwłaszcza przy pracy z zestawami znaków niewykorzystujących kodowania ASCII.