透過示例學習-Go-語言-2023-二-

绝不原创的飞龙發表於2024-10-19

透過示例學習 Go 語言 2023(二)

Go(Golang)中的所有基本資料型別

來源:golangbyexample.com/all-basic-data-types-golang/

這是 Golang 綜合教程系列的第七章。有關該系列其他章節的資訊,請參閱此連結 – Golang 綜合教程系列

下一教程函式

前一教程變數

現在讓我們檢視當前教程。以下是當前教程的目錄。

目錄

概述

  • 基本型別

    • 整數

      • 有符號整數

      • 無符號

    • 浮點數

    • 複數

    • 位元組

    • 字元

    • 字串

    • 布林值

  • 結論

概述

Golang 是一種靜態型別的程式語言,意味著每個變數都有一個型別。Go 有幾種內建型別。Go 中的資料型別可以分為兩種型別。

  1. 基本型別

  2. 複合型別

  • 基本型別

    • 整數

      • 有符號

        • 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.Durationint64 型別。

無符號

無符號整數有 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 中,字元 ab 使用 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 中的資料型別可以分為兩種型別。

  1. 基本型別

  2. 複合型別

  • 基本型別

    • 整數

      • 有符號

        • 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 中,字元 ab 使用 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 中的資料型別。

  • 側邊目錄***

相關文章