透過示例學習 Go 語言 2023(二)
Go(Golang)中的所有基本資料型別
來源:
golangbyexample.com/all-basic-data-types-golang/
這是 Golang 綜合教程系列的第七章。有關該系列其他章節的資訊,請參閱此連結 – Golang 綜合教程系列
下一教程 – 函式
前一教程 –變數
現在讓我們檢視當前教程。以下是當前教程的目錄。
目錄
概述
-
基本型別
-
整數
-
有符號整數
-
無符號
-
-
浮點數
-
複數
-
位元組
-
字元
-
字串
-
布林值
-
-
結論
概述
Golang 是一種靜態型別的程式語言,意味著每個變數都有一個型別。Go 有幾種內建型別。Go 中的資料型別可以分為兩種型別。
-
基本型別
-
複合型別
-
基本型別
-
整數
-
有符號
-
int
-
int8
-
int16
-
int32
-
int64
-
-
無符號
-
uint
-
uint8
-
uint16
-
uint32
-
uint64
-
uintptr
-
-
-
浮點數
-
float32
-
float64
-
-
複數
-
complex64
-
complex128
-
-
位元組
-
字元
-
字串
-
布林值
-
-
複合型別
-
集合/聚合或非引用型別
-
陣列
-
結構體
-
-
引用型別
-
切片
-
對映
-
通道
-
指標
-
函式/方法
-
-
介面
- 空介面的特殊情況
-
基本型別
在本文中,我們將僅討論基本型別。
整數
整數可以是有符號或無符號。
有符號整數
有符號整數有 5 種型別,如下所示
型別 | 大小 |
---|---|
int | 依賴於平臺 |
int8 | 8 位/1 位元組 |
int16 | 16 位/2 位元組 |
int32 | 32 位/4 位元組 |
int64 | 64 位/8 位元組 |
int
大小: 依賴於平臺。
-
在 32 位機器上,int 的大小將為 32 位或 4 位元組。
-
在 64 位機器上,int 的大小將為 64 位或 8 位元組。
範圍:再次取決於平臺
-
在 32 位機器上,int 的大小將為 32 位或 4 位元組。
-
在 64 位機器上,int 的大小將為 64 位或 8 位元組。
使用時機:
-
在使用有符號整數時,建議使用 int,除了下面提到的情況。
-
當機器是 32 位且所需範圍大於-231 到 231-1 時,則使用 int64 而不是 int。請注意,在這種情況下,int64 需要兩個 32 位記憶體地址來共同形成一個 64 位數字。
-
當範圍較小時,使用適當的整數型別。
-
屬性:
- 宣告一個 int
var a int
- int 是整數的預設型別。當你沒有指定型別時,預設型別為 int。
b := 2 //The default is also intfmt.Println(reflect.TypeOf(b)) => int
- bits包可以幫助瞭解系統上int的大小
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64sizeOfIntInBits := bits.UintSizefmt.Println(sizeOfIntInBits) => 32 0r 34
- unsafe.Sizeof() 函式也可以用於檢視 int 的位元組大小。
完整工作程式碼
下面是上述屬性的完整工作程式碼
package main
import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)
func main() {
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfIntInBits := bits.UintSize
fmt.Printf("%d bits\n", sizeOfIntInBits)
var a int
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
b := 2
fmt.Printf("b's typs is %s\n", reflect.TypeOf(b))
}
輸出:
64 bits
8 bytes
a's type is int
b's typs is int
int8
大小: 8 位或 1 位元組
範圍: -2⁷ 到 2⁷ - 1。
何時使用:
-
當已知整數範圍在 -2⁷ 到 2⁷ - 1 之間時,使用 int8。對於臨時值,如迴圈不變數,儘管可能佔用更多空間,仍建議使用 int,因為在某些操作或庫呼叫中它可能會被提升為 int。
-
對於範圍在 -27 到 27 - 1 之間的陣列值,使用 int8 是一個好的用例。例如,如果你要儲存小寫字母的 ASCII 索引,則可以使用 int8。
-
對於資料值,使用 int8 是個好主意。
int16
大小: 16 位或 2 位元組
範圍: -2¹⁵ 到 2¹⁵ - 1。何時使用:
-
當已知整數範圍在 -2¹⁵ 到 2¹⁵ - 1 之間時,使用 int16。對於臨時值,如迴圈不變數,儘管可能佔用更多空間,仍建議使用 int,因為在某些操作或庫呼叫中它可能會被提升為 int。
-
對於範圍在 -215 到 215 - 1 之間的陣列值,使用 int8 是一個好的用例。例如,如果你要儲存小寫字母的 ASCII 索引,則可以使用 int16。
int32
大小: 32 位或 4 位元組
範圍: -2³¹ 到 2³¹ - 1。
int64
大小: 64 位或 8 位元組
範圍: -2⁶³ 到 2⁶³ - 1。何時使用:
- int64 在範圍更高時使用。例如,time.Duration 是 int64 型別。
無符號
無符號整數有 5 種型別,如下所示。
型別 | 大小 |
---|---|
uint | 平臺相關 |
uint8 | 8 位/1 位元組 |
uint16 | 16 位/2 位元組 |
uint32 | 32 位/4 位元組 |
uint64 | 64 位/8 位元組 |
uint
大小: 平臺相關。
-
在 32 位機器上,int 的大小將是 32 位或 4 位元組。
-
在 64 位機器上,int 的大小將是 64 位或 8 位元組。
範圍:再次平臺相關。
-
在 32 位機器上,int 的範圍將是 -2³¹ 到 2³¹ - 1。
-
在 64 位機器上,int 的範圍將是 -2⁶³ 到 2⁶³ - 1
何時使用:
-
每當使用帶符號整數時,除了以下提到的情況,使用 uint 是個好主意。
-
當機器為 32 位且所需範圍大於 -2³¹ 到 2³¹ - 1 時,使用 int64 而不是 int。請注意,在這種情況下,int64 由 2 個 32 位記憶體地址組合而成一個 64 位數字。
-
當範圍較小時,使用適當的 int 型別。
-
屬性:
- 宣告一個 uint
var a uint
- golang 的 bits 包可以幫助瞭解系統中 uint 的大小。
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64sizeOfUintInBits := bits.UintSizefmt.Println(sizeOfIntInBits) => 32 or 64
- unsafe.Sizeof() 函式也可以用於檢視 uint 的位元組大小。
完整工作程式碼
下面是上述屬性的完整工作程式碼
package main
import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)
func main() {
//This is computed as const uintSize = 32 << (^uuint(0) >> 32 & 1) // 32 or 64
sizeOfuintInBits := bits.UintSize
fmt.Printf("%d bits\n", sizeOfuintInBits)
var a uint
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
64 bits
8 bytes
a's type is uint
uintptr
這是一種無符號整數型別,足夠大以容納任何指標地址。因此其大小和範圍與平臺相關。
大小: 平臺相關。
-
在 32 位機器上,int 的大小將是 32 位或 4 位元組。
-
在 64 位機器上,int 的大小將是 64 位或 8 位元組。
範圍:再次取決於平臺
-
在 32 位機器上,int 的範圍將是 -2³¹ 到 2³¹ - 1。
-
在 64 位機器上,int 的範圍將是 -2⁶³ 到 2⁶³ - 1。
屬性:
-
uintptr 可以轉換為 unsafe.Pointer,反之亦然。
-
可以對 uintptr 執行算術運算
-
uintptr 儘管它持有指標地址,但僅僅是一個值,並不引用任何物件。因此
-
如果相應的物件移動,其值不會被更新。例如,當 goroutine 堆疊改變時。
-
相應的物件可以被垃圾回收。
-
何時使用:
*** 其目的主要是與 unsafe.Pointer 一起使用,用於不安全的記憶體訪問。
- 當你想儲存指標地址值以進行列印或儲存時。由於地址僅被儲存並不引用任何物件,相應的物件可以被垃圾回收。
完整工作程式碼
package main
import (
"fmt"
"unsafe"
)
type sample struct {
a int
b string
}
func main() {
s := &sample{a: 1, b: "test"}
//Getting the address of field b in struct s
p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.b))
//Typecasting it to a string pointer and printing the value of it
fmt.Println(*(*string)(p))
}
輸出
test
uint8
大小: 8 位或 1 位元組
範圍:0 到 255 或 0 到 2⁸ - 1。何時使用:
-
當已知 int 的範圍將介於 2⁸ - 1 之間時使用 uint8。對於臨時值,如迴圈不變數,儘管可能佔用更多空間,仍建議使用 int,因為它在某些操作或庫呼叫中可能會提升為 int。
-
對於值在 2⁸ - 1 之間的陣列,使用 uint8 是一個不錯的選擇。例如,如果你在陣列中儲存 ASCII 索引,則可以使用 uint8。
uint16
大小: 16 位或 2 位元組
範圍:0 到 2¹⁶ - 1。何時使用:
-
當已知 int 的範圍將介於 0 到 2¹⁶ - 1 之間時使用 int16。對於臨時值,如迴圈不變數,儘管可能佔用更多空間,仍建議使用 int,因為它在某些操作或庫呼叫中可能會提升為 int。
-
對於值在 -0 到 2¹⁶ - 1 之間的陣列,使用 int8 是一個不錯的選擇。
uint32
大小: 32 位或 4 位元組
範圍:0 到 2³² - 1
uint64
大小: 64 位或 8 位元組
範圍:0 到 2⁶⁴ - 1。何時使用:
- 當範圍較高時使用 uint64。
浮點數
浮點數是帶有小數的數字。它有兩種型別。
型別 | 大小 |
---|---|
float32 | 32 位或 4 位元組 |
float64 | 64 位或 8 位元組 |
float64 是預設的浮點型別。當你用小數值初始化一個變數且未指定浮點型別時,推斷出的預設型別將是 float64。
float32
float32 使用單精度浮點格式來儲存值。基本上,它是所有 IEEE-754 32 位浮點數的集合。32 位被分為:1 位符號位、8 位指數位和 23 位尾數。float 32 的大小是 float 64 的一半,並且在某些機器架構上速度相對更快。
大小:32 位或 4 位元組
範圍:1.2E-38 到 3.4E+38
預設值:0.0
何時使用:
如果在你的系統中記憶體是瓶頸且範圍較小,則可以使用 float32。
示例:
以下程式碼示例說明了以下幾點。
-
宣告一個 float32。
-
列印 float32 的大小(位元組)。
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a float32
var a float32 = 2
//Size of float32 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
4 bytes
a's type is float32
float64
float64 使用雙精度浮點格式來儲存值。基本上它是所有 IEEE-754 64 位浮點數的集合。這 64 位分為 1 位符號、11 位指數和 52 位尾數。float64 的大小是 float32 的兩倍,但能比 float32 更準確地表示數字。
大小:32 位或 4 位元組。
範圍:1.2E-38 到 3.4E+38。
預設值:0.0。
何時使用:
當所需精度很高時。
示例:
以下程式碼示例說明了以下幾點。
-
宣告一個 float64。
-
列印 float64 的大小(位元組)。
-
預設是 float64,當你不指定型別時。
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a float64
var a float64 = 2
//Size of float64 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
//Default is float64 when you don't specify a type
b := 2.3
fmt.Printf("b's type is %s\n", reflect.TypeOf(b))
}
輸出:
8 bytes
a's type is float64
b's type is float64
複數
複數有兩種型別。
型別 | 屬性 |
---|---|
complex64 | 實部和虛部均為 float32 |
complex128 | 實部和虛部均為 float64 |
預設複數型別是 complex128。
初始化
複數可以透過兩種方式初始化。
- 使用 complex 函式。它具有以下簽名。確保 a 和 b 的型別相同,即它們都應為 float32 或都應為 float64。
complext(a, b)
- 使用簡寫語法。這在使用直接數字建立複數時使用。如果未指定型別,使用以下方法建立的複數型別將為complex128。
a := 5 + 6i
complex64
對於 complex64,實部和虛部均為 float32。
大小:實部和虛部的大小與 float32 相同。它的大小為 32 位或 4 位元組。
範圍:實部和虛部的範圍與 float32 相同,即 1.2E-38 到 3.4E+38。
示例
以下是一個示例程式碼,顯示了。
-
如何使用上述兩種方法建立一個 complex64 數字。
-
列印一個 complex64 數字的大小。大小將是 8 位元組(4 + 4),相當於兩個 float32 數字。
-
列印一個 complex64 數字的型別。
-
對複數進行+操作。
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a float32 = 3
var b float32 = 5
//Initialize-1
c := complex(a, b)
//Initialize-2
var d complex64
d = 4 + 5i
//Print Size
fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))
//Print type
fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
fmt.Printf("d's type is %s\n", reflect.TypeOf(d))
//Operations on complex number
fmt.Println(c+d, c-d, c*d, c/d)
}
輸出:
c's size is 8 bytes
d's size is 8 bytes
c's type is complex64
d's type is complex64
(7+10i) (-1+0i) (-13+35i) (0.902439+0.12195122i)
complex128
對於 complex128,實部和虛部均為float64。
大小:實部和虛部的大小與 float64 相同。它的大小為 64 位或 8 位元組。
範圍:實部和虛部的範圍與float64相同,即-1.7E+308 到+1.7E+308。
示例
以下是一個示例程式碼,顯示了。
-
如何使用上述兩種方法建立一個 complex128 數字。當未指定型別時,預設型別將是complex128。
-
列印一個 complex128 數字的大小。大小將是 16 位元組(8 + 8),相當於兩個 float64 數字。
-
列印一個 complex128 數字的型別。
-
對複數進行不同操作。
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a float64 = 3
var b float64 = 5
//Initialize-1
c := complex(a, b)
//Initialize-2\. When don't specify a type , the default type will be complex128
d := 4 + 5i
//Print Size
fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))
//Print type
fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
fmt.Printf("d's type is %s\n", reflect.TypeOf(d))
//Operations on complex number
fmt.Println(c+d, c-d, c*d, c/d)
}
輸出:
c's size is 16 bytes
d's size is 16 bytes
c's type is complex128
d's type is complex128
(7+10i) (-1+0i) (-13+35i) (0.902439024390244+0.12195121951219513i)
位元組
Go 中的 byte 是uint8的別名,意味著它是一個整數值。這個整數值為 8 位,表示一個位元組(即 0-255 之間的數字)。因此,一個位元組可以表示 ASCII 字元。Golang 沒有‘char’的資料型別。因此。
-
位元組用於表示 ASCII 字元
-
rune 用於表示所有 UNICODE 字元,包括所有存在的字元。我們將在本教程後面研究 rune。
定義位元組
var rbyte byte := 'a'
在宣告位元組時,我們必須指定型別,就像我們在上面的程式中那樣。如果不指定型別,則預設型別為 rune。
示例
在下面的程式碼示例中:
-
如何定義位元組
-
列印位元組型別
-
列印位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var r byte = 'a'
//Print Size
fmt.Printf("Size: %d\n", unsafe.Sizeof(r))
//Print Type
fmt.Printf("Type: %s\n", reflect.TypeOf(r))
//Print Character
fmt.Printf("Character: %c\n", r)
s := "abc"
//This will the decimal value of byte
fmt.Println([]byte(s))
}
輸出:
Size: 1
Type: uint8
Character: a
[97 98 99]
Rune
Go 中的 rune 是 int32 的別名,意味著它是一個整數值。這個整數值用於表示一個 Unicode 碼點。要理解 rune,你必須知道 Unicode 是什麼。下面是簡短的描述,但你可以參考著名的博文 – 每個軟體開發者絕對、肯定必須瞭解的關於 Unicode 和字符集的最低限度知識(無藉口!)
什麼是 Unicode
Unicode 是 ASCII 字元的超集,為每個存在的字元分配一個唯一的數字。這個唯一的數字稱為 Unicode 碼點。
例如
-
數字 0 被表示為 Unicode 點 U+0030 (十進位制值 – 48)
-
小寫 b 被表示為 Unicode 點 U+0062 (十進位制值 – 98)
-
英鎊符號 £ 被表示為 Unicode 點 U+00A3 (十進位制值 – 163)
訪問 en.wikipedia.org/wiki/List_of_Unicode_characters
以瞭解其他字元的 Unicode 點。但是 Unicode 不會說明這些碼點將如何在記憶體中儲存。這就是 utf-8 進入的地方。
UTF-8
utf-8 使用 1、2、3 或 4 個位元組儲存每個 Unicode 點。ASCII 點使用 1 個位元組儲存。這就是為什麼 rune 是 int32 的別名,因為在 Go 語言中,Unicode 點最多可以佔用 4 個位元組,且每個字串都使用 utf-8 編碼。
每個 rune 旨在指代一個 Unicode 點。例如,如果你將一個字串型別轉換為 rune 陣列並列印它,那麼它將列印出每個字元的 Unicode 點。對於下面的字串 “0b£”,輸出將是 – [U+0030 U+0062 U+00A3]
fmt.Printf("%U\n", []rune("0b£"))
宣告 Rune
rune 使用單引號宣告,如下所示,宣告一個名為 ‘rPound’ 的變數。
rPound := '£'
宣告 Rune 後,你還可以執行以下操作:
- 列印型別 – 輸出將是 int32
fmt.Printf("Type: %s\n", reflect.TypeOf(rPound))
- 列印 Unicode 碼點 – 輸出將是 U+00A3
fmt.Printf("Unicode CodePoint: %U\n", rPound)
- 列印字元 – 輸出將是 £
fmt.Printf("Character: %c\n", r)
何時使用
當你打算在值中儲存 Unicode 碼點時,應該使用 rune。當陣列中的所有值都表示一個 Unicode 碼點時,應該使用 rune 陣列。
程式碼:
以下是說明我們討論的每個點的程式碼
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
r := 'a'
//Print Size
fmt.Printf("Size: %d\n", unsafe.Sizeof(r))
//Print Type
fmt.Printf("Type: %s\n", reflect.TypeOf(r))
//Print Code Point
fmt.Printf("Unicode CodePoint: %U\n", r)
//Print Character
fmt.Printf("Character: %c\n", r)
s := "0b£"
//This will print the Unicode Points
fmt.Printf("%U\n", []rune(s))
//This will the decimal value of Unicode Code Point
fmt.Println([]rune(s))
}
輸出:
Size: 4
Type: int32
Unicode CodePoint: U+0061
Character: a
[U+0030 U+0062 U+00A3]
[48 98 163]
字串
字串是 golang 中只讀的位元組切片。字串可以透過兩種方式初始化。
- 使用雙引號 “” 例如 “this”
雙引號中的字串尊重轉義序列。例如,如果字串包含 \n,則列印時會有新行。
- 使用反引號
eg \
this`
反引號中的字串只是一個原始字串,它不遵循任何型別的轉義序列。
字串中的每個字元將根據所使用的編碼佔用一些位元組。例如,在 utf-8 編碼的字串中,每個字元將佔用 1 到 4 個位元組。你可以在這篇必讀的著名部落格中閱讀關於 utf-8 的內容——每個軟體開發者絕對、肯定必須瞭解的 Unicode 和字符集的絕對最少知識(沒有藉口!)。在 utf-8 中,字元 a 或 b 使用 1 個位元組編碼,而字元英鎊符號 £ 使用 2 個位元組編碼。因此,當你將字串“ab£”轉換為位元組陣列並像下面那樣列印時,將輸出 4 個位元組。
s := "ab£"
fmt.Println([]byte(s))
輸出
[48 98 194 163]
當你嘗試使用 len(“ab£”) 列印上面字串的長度時,它將輸出 4 而不是 3,因為它包含 4 個位元組。
還要注意,range 迴圈遍歷每個字元形成的位元組序列,因此對於下面的 range 迴圈
for _, c := range s {
fmt.Println(string(c))
}
輸出將是
a
b
£
對字串可以執行許多操作。其中一種操作是連線,它將兩個字串結合在一起。連線使用符號‘+’。讓我們看看我們討論過的所有內容的完整工作程式碼。
程式碼:
package main
import (
"fmt"
)
func main() {
//String in double quotes
x := "this\nthat"
fmt.Printf("x is: %s\n", x)
//String in back quotes
y := `this\nthat`
fmt.Printf("y is: %s\n", y)
s := "ab£"
//This will print the byte sequence.
//Since character a and b occupies 1 byte each and £ character occupies 2 bytes.
//The final output will 4 bytes
fmt.Println([]byte(s))
//The output will be 4 for same reason as above
fmt.Println(len(s))
//range loops over sequences of byte which form each character
for _, c := range s {
fmt.Println(string(c))
}
//Concatenation
fmt.Println("c" + "d")
}
輸出:
x is: this
that
y is: this\nthat
[97 98 194 163]
4
a
b
£
cd
布林值
資料型別是 bool,並且有兩個可能的值 true 或 false。
預設值:false
操作:
-
並且 – &&
-
或者 – ||
-
取反 – !
示例
以下程式碼示例展示了
-
如果未初始化,預設值是 false
-
上述所有操作在布林值上
程式碼
package main
import "fmt"
func main() {
//Default value will be false it not initialized
var a bool
fmt.Printf("a's value is %t\n", a)
//And operation on one true and other false
andOperation := 1 < 2 && 1 > 3
fmt.Printf("Ouput of AND operation on one true and other false %t\n", andOperation)
//OR operation on one true and other false
orOperation := 1 < 2 || 1 > 3
fmt.Printf("Ouput of OR operation on one true and other false: %t\n", orOperation)
//Negation Operation on a false value
negationOperation := !(1 > 2)
fmt.Printf("Ouput of NEGATION operation on false value: %t\n", negationOperation)
}
輸出:
a's value is false
Ouput of AND operation on one true and other false false
Ouput of OR operation on one true and other false: true
Ouput of NEGATION operation on false value: true
結論
這就是關於 golang 的基本型別。希望你喜歡這篇文章。請在評論中分享反饋、改進意見或錯誤。
下一個教程 – 函式
上一個教程 – 變數***
Go 語言中的所有資料結構
來源:
golangbyexample.com/all-data-structures-golang/
Go 語言中的資料結構
-
佇列
-
棧
-
集合
-
連結串列
-
雙向連結串列
-
二叉搜尋樹
-
遞迴
-
迭代法
-
-
堆
-
最小堆
-
最大堆
-
-
字典樹
排序演算法
-
堆排序
-
插入排序
-
選擇排序
-
氣泡排序
整數
- 在 Go 語言中反轉一個數字或整數
字串
-
在 Go 語言中找出最長不重複字元的子串程式
-
在 Go 語言中找出字串內最長迴文子串
陣列
-
在陣列中找到兩個加起來等於目標數字的數(Go 語言)
-
在 Go 語言中找出兩個已排序陣列的中位數
連結串列
-
使用 Go 語言將單鏈錶轉換為陣列
-
使用 Go 語言將單鏈錶轉換為迴圈連結串列
-
檢查連結串列是否是迴圈連結串列(Go 語言)
-
在單連結串列中刪除前面第 k 個節點(Go 語言)
-
在單連結串列中刪除倒數第 k 個節點(Go 語言)
-
在 Go 語言中反轉雙向連結串列
-
在 Go 語言中加兩個由連結串列表示的數字
Golang 中的所有資料型別及示例
來源:
golangbyexample.com/all-data-types-in-golang-with-examples/
注意: 如果你對學習 Golang 感興趣,我們有一個全面的 Golang 教程系列,歡迎檢視 – Golang 綜合教程系列。現在讓我們來看一下當前的教程。以下是目錄。
目錄
-
概述
-
基本型別
-
整數(有符號和無符號)")
-
浮點數
-
複數
-
位元組
-
Rune
-
字串
-
布林值
-
-
複合型別
-
非引用型別
-
陣列
-
結構體
-
-
引用型別
-
切片
-
通道
-
對映
-
指標
-
函式
-
-
介面
- 空介面的特殊情況
-
-
結論
概述
Golang 是一種靜態型別程式語言,意味著每個變數都有一個型別。Go 有幾種內建型別,我們將在本文中探討。Go 中的資料型別可以分為兩種型別。
-
基本型別
-
複合型別
-
基本型別
-
整數
-
有符號
-
int
-
int8
-
int16
-
int32
-
int64
-
-
無符號
-
uint
-
uint8
-
uint16
-
uint32
-
uint64
-
uintptr
-
-
-
浮點數
-
float32
-
float64
-
-
複數
-
complex64
-
complex128
-
-
位元組
-
Rune
-
字串
-
布林值
-
-
複合型別
-
集合/聚合或非引用型別
-
陣列
-
結構體
-
-
引用型別
-
切片
-
對映
-
通道
-
指標
-
函式/方法
-
-
介面
- 空介面的特殊情況
-
基本型別
讓我們先討論一下 GO 中的基本型別。
整數(有符號和無符號)
整數可以是有符號或無符號。
有符號
有符號整數有如下五種型別:
型別 | 大小 |
---|---|
int | 平臺依賴 |
int8 | 8 位/1 位元組 |
int16 | 16 位/2 位元組 |
int32 | 32 位/4 位元組 |
int64 | 64 位/8 位元組 |
int
大小: 平臺依賴。
-
在 32 位機器上,int 的大小為 32 位或 4 位元組。
-
在 64 位機器上,int 的大小為 64 位或 8 位元組。
範圍:再次依賴於平臺。
-
在 32 位機器上,int 的範圍為-2³¹到 2³¹-1。
-
在 64 位機器上,int 的範圍為-2⁶³到 2⁶³-1。
何時使用:
-
在使用有符號整數時,建議使用 int,除非在以下提到的情況下。
-
當機器為 32 位且所需範圍大於 -2³¹ 到 2³¹ - 1 時,使用 int64 而不是 int。請注意,在這種情況下,int64 需要 2 個 32 位記憶體地址來組合成一個 64 位數字。
-
當範圍較小時,使用適當的整數型別。
-
屬性:
- 宣告一個 int
var a int
- int 是整數的預設型別。當你不指定型別時,預設型別為 int
b := 2 //The default is also int
fmt.Println(reflect.TypeOf(b)) => int
- golang 的 bits 包可以幫助瞭解系統上 int 的大小
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfIntInBits := bits.UintSize
fmt.Println(sizeOfIntInBits) => 32 0r 34
- unsafe.Sizeof() 函式也可以用來檢視 int 的位元組大小
完整工作程式碼
以下是上述屬性的完整工作程式碼
package main
import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)
func main() {
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfIntInBits := bits.UintSize
fmt.Printf("%d bits\n", sizeOfIntInBits)
var a int
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
b := 2
fmt.Printf("b's typs is %s\n", reflect.TypeOf(b))
}
輸出:
64 bits
8 bytes
a's type is int
b's typs is int
int8
大小: 8 位或 1 位元組
範圍:-2⁷ 到 2⁷ - 1。
何時使用:
-
當已知 int 範圍在 -2⁷ 到 2⁷ - 1 之間時使用 int8。對於臨時值,如迴圈不變數,仍然建議使用 int,儘管它可能佔用更多空間,因為在某些操作或庫呼叫中,它可能會被提升為 int。
-
對於值範圍在 -27 到 27 - 1 的陣列,使用 int8 是一個好用例。例如,如果你儲存小寫字母的 ASCII 索引,則可以使用 int8。
-
對於資料值,使用 int8 是一個好主意。
示例:
以下程式碼示例說明了以下幾點
-
宣告一個 int8
-
列印 int8 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a int 8
var a int8 = 2
//Size of int8 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
1 bytes
a's type is int8
int16
大小: 16 位或 2 位元組
範圍:-2¹⁵ 到 2¹⁵ - 1。
何時使用:
-
當已知 int 範圍在 -2¹⁵ 到 2¹⁵ - 1 之間時使用 int16。對於臨時值,如迴圈不變數,仍然建議使用 int,儘管它可能佔用更多空間,因為在某些操作或庫呼叫中,它可能會被提升為 int。
-
對於值範圍在 -215 到 215 - 1 的陣列,使用 int8 是一個好用例。例如,如果你儲存小寫字母的 ASCII 索引,則可以使用 int16。
示例:
以下程式碼示例說明了以下幾點
-
宣告一個 int16
-
列印 int16 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a int16
var a int16 = 2
//Size of int8 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
2 bytes
a's type is int16
int32
大小: 32 位或 4 位元組
範圍:-2³¹ 到 2³¹ - 1。
示例:
以下程式碼示例說明了以下幾點
-
宣告一個 int32
-
列印 int8 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a int32
var a int32 = 2
//Size of int32 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
4 bytes
a's type is int32
int64
大小: 64 位或 8 位元組
範圍:-2⁶³ 到 2⁶³ - 1
何時使用:
- int64 用於範圍較高的情況。例如,time.Duration 的型別是 int64
示例:
以下程式碼示例說明了以下幾點
-
宣告一個 int64
-
列印 int64 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a int64
var a int64 = 2
//Size of int64 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
8 bytes
a's type is int64
無符號
無符號整數有 5 種型別,如下所示
型別 | 大小 |
---|---|
uint | 平臺相關 |
uint8 | 8 位/1 位元組 |
uint16 | 16 位/2 位元組 |
uint32 | 32 位/4 位元組 |
uint64 | 64 位/8 位元組 |
uint
大小: 平臺相關。
-
在 32 位機器上,int 的大小將是 32 位或 4 位元組。
-
在 64 位機器上,int 的大小將是 64 位或 8 位元組
範圍:再次平臺相關
-
在 32 位機器上,int 的範圍為 -2³¹ 到 2³¹ - 1。
-
在 64 位機器上,int 的範圍將是 -2⁶³ 到 2⁶³ -1。
何時使用:
-
在使用其他有符號整數的情況下,建議使用 uint。
-
當機器為 32 位且所需範圍大於 -231 到 231 -1 時,請使用 int64 而不是 int。請注意,在這種情況下,兩個 32 位記憶體地址合成一個 64 位數字。
-
當範圍較小時,請使用適當的 int 型別。
-
屬性:
- 宣告一個 uint。
var a uint
- golang 的bits包可以幫助瞭解系統上uint的大小。
//This is computed as const uintSize = 32 << (^uint(0) >> 32 & 1) // 32 or 64
sizeOfUintInBits := bits.UintSize
fmt.Println(sizeOfIntInBits) => 32 or 64
- unsafe.Sizeof() 函式也可用於檢視 uint 的位元組大小。
完整工作程式碼
以下是上述屬性的完整工作程式碼。
package main
import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)
func main() {
//This is computed as const uintSize = 32 << (^uuint(0) >> 32 & 1) // 32 or 64
sizeOfuintInBits := bits.UintSize
fmt.Printf("%d bits\n", sizeOfuintInBits)
var a uint
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
64 bits
8 bytes
a's type is uint
uintptr
這是一種無符號整數型別,足夠大以容納任何指標地址。因此,其大小和範圍依賴於平臺。
大小: 依賴於平臺。
-
在 32 位機器上,int 的大小將為 32 位或 4 位元組。
-
在 64 位機器上,int 的大小將為 64 位或 8 位元組。
範圍:再次依賴於平臺。
-
在 32 位機器上,int 的範圍將是 -2³¹ 到 2³¹ -1。
-
在 64 位機器上,int 的範圍將是 -2⁶³ 到 2⁶³ -1。
屬性:
-
uintptr 可以轉換為unsafe.Pointer,反之亦然。
-
可以對 uintptr 執行算術運算。
-
uintptr 雖然儲存指標地址,但只是一個值,不引用任何物件。因此。
-
如果相應物件移動,其值將不會更新。例如,當 goroutine 棧變化時。
-
相應物件可以被垃圾收集。
-
何時使用:
***其目的主要是與 unsafe.Pointer 一起使用,以便進行不安全的記憶體訪問。
- 當你想儲存指標地址值以進行列印或儲存時。由於地址只是被儲存並不引用任何東西,相應的物件可以被垃圾收集。
package main
import (
"fmt"
"unsafe"
)
type sample struct {
a int
b string
}
func main() {
s := &sample{a: 1, b: "test"}
//Getting the address of field b in struct s
p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.b))
//Typecasting it to a string pointer and printing the value of it
fmt.Println(*(*string)(p))
}
輸出:
test
uint8
大小: 8 位或 1 位元組。
範圍:0 到 255 或 0 到 2⁸ -1。
何時使用:
-
當已知 int 範圍將在 2⁸ -1 之間時,使用 uint8。對於臨時值,如迴圈不變數,儘管可能佔用更多空間,但仍建議使用 int,因為在某些操作或庫呼叫中可能會提升為 int。
-
對於位於 2⁸ -1 之間的陣列值,使用 uint8 是一個不錯的用例。例如,如果你在陣列中儲存 ASCII 索引,則可以使用uint8。
示例:
以下程式碼示例說明了以下幾點。
-
宣告一個 uint8。
-
列印 uint8 的位元組大小。
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a uint8
var a uint8 = 2
//Size of uint8 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
1 bytes
a's type is uint8
uint16
大小: 16 位或 2 位元組。
範圍:0 到 2¹⁶ -1。
何時使用:
-
當已知 int 範圍將在 0 到 2¹⁶ -1 之間時,使用 int16。對於臨時值,如迴圈不變數,儘管可能佔用更多空間,但仍建議使用 int,因為在某些操作或庫呼叫中可能會提升為 int。
-
對於位於 -0 到 2¹⁶ -1 之間的陣列值,使用 int8 是一個不錯的用例。
示例:
以下程式碼示例說明了以下要點
-
宣告一個 uint16
-
列印 uint16 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a uint16
var a uint16 = 2
//Size of uint16 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
2 bytes
a's type is uint16
uint32
大小: 32 位或 4 位元組
範圍:0 到 2³² -1
示例:
以下程式碼示例說明了以下要點
-
宣告一個 uint32
-
列印 uint32 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a uint32
var a uint32 = 2
//Size of uint32 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
1 bytes
a's type is uint32
uint64
大小: 64 位或 8 位元組
範圍:0 到 2⁶⁴ -1
使用時機:
- uint64 在範圍更高時使用。
示例:
以下程式碼示例說明了以下要點
-
宣告一個 uint64
-
列印 uint64 的位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a uint64
var a uint64 = 2
//Size of uint64 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
8 bytes
a's type is uint64
浮點數
浮點數是帶小數的數字。它有兩種型別
型別 | 大小 |
---|---|
float32 | 32 位或 4 位元組 |
float64 | 64 位或 8 位元組 |
float64是預設的浮點型別。當你用小數值初始化變數而不指定浮點型別時,推斷的預設型別將是float64。
float32
float32使用單精度浮點格式儲存值。基本上,它是所有 IEEE-754 32 位浮點數的集合。32 位被劃分為– 1 位符號位,8 位指數,和 23 位尾數。float32 佔用的空間是 float64 的一半,在某些機器架構上速度相對較快。
大小:32 位或 4 位元組
範圍:1.2E-38 到 3.4E+38
預設值:0.0
使用時機:
- 如果系統記憶體是瓶頸且範圍較小,則可以使用float32。
示例:
以下程式碼示例說明了以下要點
-
宣告一個 float32
-
列印 float32 的位元組大小
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a float32
var a float32 = 2
//Size of float32 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
}
輸出:
4 bytes
a's type is float32
float64
float64 使用雙精度浮點格式儲存值。基本上,它是所有 IEEE-754 64 位浮點數的集合。64 位被劃分為– 1 位符號位,11 位指數,52 位尾數。float64 佔用的空間是 float32 的兩倍,但可以比 float32 更準確地表示數字。
大小:32 位或 4 位元組
範圍:1.2E-38 到 3.4E+38
預設值:0.0
使用時機:
- 當需要高精度時
示例:
以下程式碼示例說明了以下要點
-
宣告一個 float64
-
列印 float64 的位元組大小
-
當你不指定型別時,預設是 float64
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
//Declare a float64
var a float64 = 2
//Size of float64 in bytes
fmt.Printf("%d bytes\n", unsafe.Sizeof(a))
fmt.Printf("a's type is %s\n", reflect.TypeOf(a))
//Default is float64 when you don't specify a type
b := 2.3
fmt.Printf("b's type is %s\n", reflect.TypeOf(b))
}
輸出:
8 bytes
a's type is float64
b's type is float64
複數
複數有兩種型別
型別 | 屬性 |
---|---|
complex64 | 實部和虛部都是 float32 |
complex128 | 實部和虛部都是 float64 |
預設的複數型別是 complex128。
初始化
複數可以透過兩種方式初始化
- 使用複數函式。它具有以下簽名。請確保 a 和 b 的型別相同,意味著它們要麼都是 float32,要麼都是 float64。
complext(a, b)
- 使用簡寫語法。這在使用直接數字建立複數時使用。如果未指定型別,則使用下面的方法建立的複數型別將為complex128。
a := 5 + 6i
complex64
對於 complex 64,實部和虛部都是 float32
大小:實部和虛部的大小與 float32 相同。大小為 32 位或 4 位元組
範圍:實部和虛部的範圍與 float32 相同,即 1.2E-38 到 3.4E+38
示例
以下是顯示的示例程式碼
-
如何使用上述兩種方法建立 complex64 數字
-
列印 complex64 數字的大小。大小將為 8 位元組(4 + 4),相當於兩個 float32 數字
-
列印 complex64 數字的型別
-
- 運算子在複數上
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a float32 = 3
var b float32 = 5
//Initialize-1
c := complex(a, b)
//Initialize-2
var d complex64
d = 4 + 5i
//Print Size
fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))
//Print type
fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
fmt.Printf("d's type is %s\n", reflect.TypeOf(d))
//Operations on complex number
fmt.Println(c+d, c-d, c*d, c/d)
}
輸出:
c's size is 8 bytes
d's size is 8 bytes
c's type is complex64
d's type is complex64
(7+10i) (-1+0i) (-13+35i) (0.902439+0.12195122i)
complex128
對於 complex128,實部和虛部都是 float64
大小:實部和虛部的大小與 float64 相同。大小為 64 位或 8 位元組
範圍:實部和虛部的範圍與 float64 相同,即 -1.7E+308 到 +1.7E+308
示例
以下是顯示的示例程式碼
-
如何使用上述兩種方法建立 complex128 數字。它還顯示當型別未指定時,預設型別將為 complex128
-
列印 complex128 數字的大小。大小將為 16 位元組(8 + 8),相當於兩個 float64 數字
-
列印 complex128 數字的型別
-
對複數進行不同操作
程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a float64 = 3
var b float64 = 5
//Initialize-1
c := complex(a, b)
//Initialize-2\. When don't specify a type , the default type will be complex128
d := 4 + 5i
//Print Size
fmt.Printf("c's size is %d bytes\n", unsafe.Sizeof(c))
fmt.Printf("d's size is %d bytes\n", unsafe.Sizeof(d))
//Print type
fmt.Printf("c's type is %s\n", reflect.TypeOf(c))
fmt.Printf("d's type is %s\n", reflect.TypeOf(d))
//Operations on complex number
fmt.Println(c+d, c-d, c*d, c/d)
}
輸出:
c's size is 16 bytes
d's size is 16 bytes
c's type is complex128
d's type is complex128
(7+10i) (-1+0i) (-13+35i) (0.902439024390244+0.12195121951219513i)
位元組
Go 中的位元組是 uint8 的別名,意味著它是一個整數值。這個整數值為 8 位,表示一個位元組,即 0-255 之間的數字。因此,一個位元組可以表示 ASCII 字元。Golang 沒有 ‘char’ 的資料型別。因此
-
位元組用於表示 ASCII 字元
-
rune 用於表示所有 Unicode 字元,包括所有存在的字元。我們將在本教程後面學習 rune。
定義位元組
var rbyte byte := 'a'
在宣告位元組時,我們必須指定型別,如上面的程式所示。如果不指定型別,則預設型別被視為 rune。
示例
在下面的程式碼示例中:
-
如何定義位元組
-
列印位元組型別
-
列印位元組大小
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var r byte = 'a'
//Print Size
fmt.Printf("Size: %d\n", unsafe.Sizeof(r))
//Print Type
fmt.Printf("Type: %s\n", reflect.TypeOf(r))
//Print Character
fmt.Printf("Character: %c\n", r)
s := "abc"
//This will the decimal value of byte
fmt.Println([]byte(s))
}
輸出:
Size: 1
Type: uint8
Character: a
[97 98 99]
符文
Go 中的 rune 是 int32 的別名,意味著它是一個整數值。這個整數值用來表示 Unicode 程式碼點。要理解 rune,您必須瞭解 Unicode。下面是簡短的描述,您可以參考著名的部落格文章 – 每個軟體開發人員絕對必須瞭解的 Unicode 和字符集的絕對最小知識(沒有藉口!)
什麼是 Unicode
Unicode 是 ASCII 字元的超集,它為每個存在的字元分配一個唯一的數字。這個唯一的數字稱為 Unicode 程式碼點。
例如
-
數字 0 表示為 Unicode 點 U+0030 (十進位制值 – 48)
-
小寫 b 表示為 Unicode 點 U+0062 (十進位制值 – 98)
-
英鎊符號 £ 表示為 Unicode 點 U+00A3 (十進位制值 – 163)
訪問 en.wikipedia.org/wiki/List_of_Unicode_characters
瞭解其他字元的 Unicode 點。但是 Unicode 不討論這些程式碼點如何儲存在記憶體中。這就是 utf-8 進入的地方。
UTF-8
utf-8 使用 1、2、3 或 4 個位元組儲存每個 Unicode 點。ASCII 點使用 1 個位元組儲存。這就是為什麼 rune 是 int32 的別名,因為在 Go 中,Unicode 點的最大位元組數為 4,而 Go 中的每個字串都是使用 utf-8 編碼的。
每個 rune 旨在指代一個 Unicode 點。例如,如果你在將字串強制轉換為 rune 陣列後列印它,則會列印每個字元的 Unicode 點。對於下面的字串 “0b£”,輸出將是 – [U+0030 U+0062 U+00A3]。
fmt.Printf("%U\n", []rune("0b£"))
宣告 Rune
一個 rune 是透過在單引號中宣告的字元,例如宣告一個名為 ‘rPound’ 的變數。
rPound := '£'
宣告 Rune 後,你還可以執行以下操作。
- 列印型別 – 輸出將是 int32。
fmt.Printf("Type: %s\n", reflect.TypeOf(rPound))
- 列印 Unicode 程式碼點 – 輸出將是 U+00A3。
fmt.Printf("Unicode CodePoint: %U\n", rPound)
- 列印字元 – 輸出將是 £。
fmt.Printf("Character: %c\n", r)
何時使用
當你打算在值中儲存 Unicode 程式碼點時,應使用一個 rune。當陣列中的所有值都旨在表示 Unicode 程式碼點時,應使用 rune 陣列。
程式碼:
以下是說明我們討論的每一點的程式碼。
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
r := 'a'
//Print Size
fmt.Printf("Size: %d\n", unsafe.Sizeof(r))
//Print Type
fmt.Printf("Type: %s\n", reflect.TypeOf(r))
//Print Code Point
fmt.Printf("Unicode CodePoint: %U\n", r)
//Print Character
fmt.Printf("Character: %c\n", r)
s := "0b£"
//This will print the Unicode Points
fmt.Printf("%U\n", []rune(s))
//This will the decimal value of Unicode Code Point
fmt.Println([]rune(s))
}
輸出:
Size: 4
Type: int32
Unicode CodePoint: U+0061
Character: a
[U+0030 U+0062 U+00A3]
[48 98 163]
字串
字串是 golang 中只讀的位元組切片。字串可以透過兩種方式初始化。
- 使用雙引號 “” 例如 “this”。
雙引號中的字串會遵循轉義序列。例如,如果字串包含 \n,那麼在列印時會產生新的一行。
- 使用反引號
例如 \
this`。
反引號中的字串只是一個原始字串,不會遵循任何型別的轉義序列。
字串中的每個字元將根據使用的編碼佔用一些位元組。例如,在 utf-8 編碼的字串中,每個字元將佔用 1 到 4 個位元組。你可以閱讀關於 utf-8 的必讀部落格——每個軟體開發者必須瞭解的 Unicode 和字符集的絕對最小知識(沒有藉口!)。在 utf-8 中,字元 a 或 b 使用 1 個位元組編碼,而字元英鎊符號 £ 使用 2 個位元組編碼。因此,字串 “ab£” 在轉換為位元組陣列並列印時將輸出 4 個位元組。
s := "ab£"
fmt.Println([]byte(s))
輸出
[48 98 194 163]
當你嘗試使用 len(“ab£”) 列印上述字串的長度時,它將輸出 4 而不是 3,因為它包含 4 個位元組。
還要注意,range 迴圈遍歷每個字元形成的位元組序列,因此對於下面的 range 迴圈。
for _, c := range s {
fmt.Println(string(c))
}
輸出將是
a
b
£
可以對字串執行許多操作。其中一種操作是連線,它將兩個字串合併。連線使用符號 ‘+’。讓我們看看我們討論的所有內容的完整工作程式碼。
程式碼:
package main
import (
"fmt"
)
func main() {
//String in double quotes
x := "this\nthat"
fmt.Printf("x is: %s\n", x)
//String in back quotes
y := `this\nthat`
fmt.Printf("y is: %s\n", y)
s := "ab£"
//This will print the byte sequence.
//Since character a and b occupies 1 byte each and £ character occupies 2 bytes.
//The final output will 4 bytes
fmt.Println([]byte(s))
//The output will be 4 for same reason as above
fmt.Println(len(s))
//range loops over sequences of byte which form each character
for _, c := range s {
fmt.Println(string(c))
}
//Concatenation
fmt.Println("c" + "d")
}
輸出:
x is: this
that
y is: this\nthat
[97 98 194 163]
4
a
b
£
cd
布林值
資料型別是bool,有兩個可能的值:true 或 false。
預設值:false。
操作:
-
和 – &&
-
或 – ||
-
取反 – !
示例
以下程式碼示例顯示了。
-
如果未初始化,預設值為false。
-
所有上述操作在 bool 上。
程式碼
package main
import "fmt"
func main() {
//Default value will be false it not initialized
var a bool
fmt.Printf("a's value is %t\n", a)
//And operation on one true and other false
andOperation := 1 < 2 && 1 > 3
fmt.Printf("Ouput of AND operation on one true and other false %t\n", andOperation)
//OR operation on one true and other false
orOperation := 1 < 2 || 1 > 3
fmt.Printf("Ouput of OR operation on one true and other false: %t\n", orOperation)
//Negation Operation on a false value
negationOperation := !(1 > 2)
fmt.Printf("Ouput of NEGATION operation on false value: %t\n", negationOperation)
}
輸出:
a's value is false
Ouput of AND operation on one true and other false false
Ouput of OR operation on one true and other false: true
Ouput of NEGATION operation on false value: true
複合型別
非引用型別
陣列
Go 中的陣列是值。它們是固定長度的同型別序列。由於 Go 中的陣列是值,這就是原因。
-
當你將一個陣列賦值給另一個變數時,它會複製整個陣列。
-
當你將陣列作為引數傳遞給一個函式時,它會複製整個陣列,而不僅僅是傳遞地址。
陣列的宣告如下。假設 N 是陣列的大小。
- 同時指定大小和數值。
newArray := [n]Type{val1, val2, val3}
- 指定大小 – 無數值。值被設定為該型別的預設零值。
newArray := [len]Type{}
如我們所說,陣列的長度是固定的。因此。
-
我們不能將一個陣列賦值給同型別但長度不同的另一個陣列。
-
當你將陣列作為函式引數傳遞時,大小也是其中的一部分。
讓我們來看一個陣列的例子。以下示例。
-
演示如何宣告一個陣列。
-
將陣列作為函式引數傳遞。
package main
import "fmt"
func main() {
//Declare a array
sample := [3]string{"a", "b", "c"}
print(sample)
}
func print(sample [3]string) {
fmt.Println(sample)
}
輸出:
[a b c]
結構體
在 Go 中,結構體是欄位的集合。這些欄位可以是不同型別的。結構體作為異構資料型別相關資料的容器。例如,不同的屬性用來表示組織中的員工。員工可以有。
-
字串型別的名稱。
-
整數型別的年齡。
-
時間型別的出生日期(DOB)。
..等等。結構體可以用來表示一個員工。
type employee struct {
name string
age int
dob time.Time
}
以下程式描述了。
-
宣告一個結構體。
-
以不同方式初始化結構體。
-
結構體的大小是其欄位大小的總和。
package main
import (
"fmt"
)
//Declare a struct
type employee struct {
name string
age int
salary float64
}
func main() {
//Initialize a struct without named fields
employee1 := employee{"John", 21, 1000}
fmt.Println(employee1)
//Initialize a struct with named fields
employee2 := employee{
name: "Sam",
age: 22,
salary: 1100,
}
fmt.Println(employee2)
//Initializing only some fields. Other values are initialized to default zero value of that type
employee3 := employee{name: "Tina", age: 24}
fmt.Println(employee3)
}
輸出:
{John 21 1000}
{Sam 22 1100}
{Tina 24 0}
引用型別
切片
切片的大小是動態的,引用陣列的元素。如上所述,陣列是固定大小的,因此切片為陣列提供了更靈活的介面。切片是一種引用型別,因為它內部引用一個陣列。它內部由三個欄位表示。
-
指向基礎陣列的地址。
-
切片的長度。
-
切片的容量。
切片的型別由基礎陣列元素的型別決定,而不是由其長度或容量決定。因此,如果基礎陣列的型別相同,不論其長度和容量如何,兩個切片將具有相同的型別。內建的append函式可以用來向基礎陣列新增更多值。如果在使用append函式時切片的長度超過當前容量,則分配一個新的切片,容量為當前容量的兩倍,當前切片的元素被複制到該新切片中。
內建函式len可以用來獲取當前切片的長度,初始化切片。
- 使用 make – 它幫助你建立一個切片,指定陣列的型別、長度和容量。指定長度和容量是可選的。如果指定了長度而未指定容量,則容量將等於長度。
make([]TYPE, length, capacity)
- 直接初始化。以下示例建立一個字串的切片。
p := []string{"a", "b", "c"}
切片也可以從陣列或其他切片建立。
下面是一個程式,展示了一個切片的示例
-
使用上述方法宣告一個切片
-
顯示追加函式
-
如何遍歷一個切片
package main
import "fmt"
func main() {
//Declare a slice using make
s := make([]string, 2, 3)
fmt.Println(s)
//Direct intialization
p := []string{"a", "b", "c"}
fmt.Println(p)
//Append function
p = append(p, "d")
fmt.Println(p)
//Iterate over a slcie
for _, val := range p {
fmt.Println(val)
}
}
輸出:
[ ]
[a b c]
[a b c d]
a
b
c
d
通道
通道提供了 goroutine 之間的同步和通訊。你可以將其視為一個管道,透過這個管道,goroutine 可以傳送和接收值。運算子<-用於傳送或接收,箭頭的方向指定了資料流的方向。
ch <- val //Sending a value present in var variable to channel
val := <-cha //Receive a value from the channel and assign it to val variable
通道有兩種型別
-
無緩衝通道 - 它沒有任何容量來儲存值,因此
-
在通道上傳送操作會阻塞,除非有另一個 goroutine 來接收。
-
接收在另一側有另一個 goroutine 傳送之前會阻塞。
-
-
緩衝通道 - 你可以在這裡指定緩衝區的大小
-
僅當緩衝區滿時,傳送到緩衝通道才會阻塞
-
當通道的緩衝區為空時,接收是唯一會阻塞的操作
-
一個通道一次只能持有特定型別的資料。在建立通道時,必須在初始化新通道時指定資料型別。在下面的示例中,我們建立了一個持有字串型別資料的通道。
events := make(chan string) //Unbuffered channel
events2 := make(chan string, 2) //Buffered channel of length 2
關閉通道
close() 函式可用於關閉通道。關閉通道意味著不能再向通道傳送值
讓我們看一個同時使用緩衝和無緩衝通道的工作程式碼示例
緩衝通道示例:
package main
import "fmt"
func main() {
//Creating a buffered channel of length 3
eventsChan := make(chan string, 3)
eventsChan <- "a"
eventsChan <- "b"
eventsChan <- "c"
//Closing the channel
close(eventsChan)
for event := range eventsChan {
fmt.Println(event)
}
}
輸出:
a
b
c
無緩衝通道示例:
package main
import "fmt"
func main() {
eventsChan := make(chan string)
go sendEvents(eventsChan)
for event := range eventsChan {
fmt.Println(event)
}
}
func sendEvents(eventsChan chan<- string) {
eventsChan <- "a"
eventsChan <- "b"
eventsChan <- "c"
close(eventsChan)
}
輸出:
a
b
c
對映
對映是 Go 語言內建的資料型別,類似於雜湊,對映鍵到值。對映是引用資料型別。當你將一個對映賦值給另一個對映時,兩個對映都引用同一個底層對映。
零值
對映的零值是 nil
宣告
- 可以使用 var 關鍵字宣告一個對映,並指定其鍵和值的型別。例如,下面的對映宣告瞭一個名稱為
var employeeSalary map[string]int
初始化
- 使用 make
var employeeSalary make(map[string]int)
- 使用大括號。你可以在對映中指定對映字面量值,也可以留空的大括號
//Empty braces
employeeSalary := map[string]int{}
//Specify values
employeeSalary := map[string]int{
"John": 1000
"Sam": 2000
}
操作
- 新增到一個對映
employeeSalary["John"] = 1000
- 從對映中獲取
salary := employeeSalary["John"]
- 從對映中刪除一個鍵
delete(employeeSalary, "John")
示例
下面的示例展示了我們討論過的所有要點
package main
import "fmt"
func main() {
//Declare
var employeeSalary map[string]int
fmt.Println(employeeSalary)
//Intialize using make
employeeSalary2 := make(map[string]int)
fmt.Println(employeeSalary2)
//Intialize using map lieteral
employeeSalary3 := map[string]int{
"John": 1000,
"Sam": 1200,
}
fmt.Println(employeeSalary3)
//Operations
//Add
employeeSalary3["Carl"] = 1500
//Get
fmt.Printf("John salary is %d\n", employeeSalary3["John"])
//Delete
delete(employeeSalary3, "Carl")
//Print map
fmt.Println("\nPrinting employeeSalary3 map")
fmt.Println(employeeSalary3)
}
輸出
map[]
map[]
map[John:1000 Sam:1200]
John salary is 1000
Printing employeeSalary3 map
map[John:1000 Sam:1200]
指標
指標是一個變數,它儲存另一個變數的記憶體地址。指標的零值是 nil。
宣告一個指標
在下面的示例中,ex 是 int 指標。
var ex *int
初始化
& 用於獲取變數的地址
a := 2
b := &b
- 運算子可用於解引用指標,這意味著獲取儲存在指標地址中的值。
fmt.Println(*b) //Print the value stored at address b
指標也可以使用 new 運算子初始化
a := new(int)
*a = 10
fmt.Println(*a) //Output will be 10
讓我們來看一個涵蓋上述所有要點的工作程式碼
package main
import "fmt"
func main() {
//Declare
var b *int
a := 2
b = &a
//Will print a address. Output will be different everytime.
fmt.Println(b)
fmt.Println(*b)
b = new(int)
*b = 10
fmt.Println(*b)
}
輸出:
0xc000018080
2
10
函式
在 Go 中,函式是值,可以像值一樣傳遞。基本上,函式可以作為一等物件使用並被傳遞。函式的簽名是
func some_func_name(arguments) return_values
函式有一個名稱、引數和返回值。此外,請注意 Go 中方法和函式之間的一些重要區別。讓我們看看一個方法的簽名。
方法:
func (receiver receiver_type) some_func_name(arguments) return_values
從上述簽名可以看出,該方法有一個接收者引數。接收者可以是一個結構體或其他任何型別。該方法將訪問接收者的屬性,並可以呼叫接收者的其他方法。
下面是一個函式的工作示例。
package main
import "fmt"
func main() {
add := func(x, y int) int {
return x + y
}
fmt.Println(add(1, 2))
}
func doOperation(fn func(int, int) int, x, y int) int {
return fn(x, y)
}
輸出:
3
介面
介面是 Go 中的一種型別,是方法簽名的集合。任何實現了介面所有方法的型別都屬於該介面型別。介面的零值是 nil。
介面的簽名
type name_of_interface interface{
//Method signature 1
//Method signature 2
}
介面是隱式實現的
型別實現介面沒有明確的宣告。事實上,在 Go 中並沒有類似於 Java 的 “implements” 關鍵字。如果一個型別實現了介面的所有方法,那麼它就實現了該介面。
定義一個介面型別的變數是正確的,如果具體型別實現了介面的所有方法,我們可以將任何具體型別的值分配給該變數。讓我們來看一個介面的工作示例。在下面的程式中。
-
我們宣告一個名為 shape 的介面,其中包含一個方法 area。
-
square 結構體實現了 area 方法,因此它隱式實現了 shape 介面。
-
我們宣告一個名為 "s" 的 shape 型別變數。
-
s 被分配了具體型別 square 的值。這是可行的,因為 square 結構體實現了 shape 介面的所有方法。
package main
import "fmt"
type shape interface {
area() int
}
type square struct {
side int
}
func (s *square) area() int {
return s.side * s.side
}
func main() {
var s shape
s = &square{side: 4}
fmt.Println(s.area())
}
輸出:
16
空介面的特殊情況
一個空介面沒有方法,因此預設情況下所有具體型別都實現了空介面。如果您編寫一個接受空介面的函式,那麼您可以將任何型別傳遞給該函式。請參見下面的工作程式碼:
package main
import "fmt"
func main() {
test("thisisstring")
test("10")
test(true)
}
func test(a interface{}) {
fmt.Printf("(%v, %T)\n", a, a)
}
輸出:
(thisisstring, string)
(10, string)
(true, bool)
結論
這就是 Go 中存在的內建資料型別。希望透過閱讀本文,您能更好地理解 Go 中的資料型別。
- 側邊目錄***