Skip to content

老陈是一个普通的文艺二逼青年

For The Dream

Golang 入门(二) —— 类型

Written by chen

Go语言内置以下这些基础类型:

  • 布尔类型:bool
  • 整型:int8、byte、int16、int、uint、uintptr
  • 浮点类型:float32、float64
  • 复数类型:complex64、complex128
  • 字符串类型:string
  • 字符类型:rune
  • 错误类型:error

此外,Go语言也支持一下这些符合类型

  • 指针(pointer)
  • 数组(array)
  • 切片(slice)
  • 字典(map)
  • 通道(chan)
  • 结构体(struct)
  • 接口(interface)

1、布尔类型

1
2
3
4
5
    var b1 bool
    b1 = true
 
    b2 := (1 == 2) // b2也会推导成bool类型
 

2、整型

1
2
3
4
5
6
7
8
9
10
11
12
13
类型              长度(字节)          值范围
int8            1                   -128 ~ 127
uint8(byte)     1                   0 ~ 255
int16           2                   -32 768 ~ 32 767
uint16          2                   0 ~ 65 535
int32           4                   -2 147 483 648 ~ 2 147 483 647
uint32          4                   0 ~ 4 294 947 295
int64           8                   -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
uint64          8                   0 ~ 18 446 744 073 709 551 615
int             平台相关                平台相关
uint            平台相关                平台相关
uintptr         同指针             在32位平台下为4字节,64位平台下为8字节
 

在Go语言中int和int32为不同的类型,以下为错误示例:

1
2
3
4
5
6
7
    // 错误示例
    var value2 int32
    value1 := 64    // value1 自动编译成int类型
    value2 = value1 // int类型赋值给int32类型,编译报错
    // 正确示例
    value2 = int32(value1)
 

同时2个不同类型的变量不能直接进行比较,但是可以直接和字面常量进行比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    var (
        i int32
        j int64
    )
    i, j = 32, 64
 
    if i == j {
        fmt.Println("i and j are equal.")
    }
 
    if i == 32 && j == 32 {
        fmt.Println("i and j are equal.")
    }
 

3、浮点类型

1
2
3
4
5
    var f1 float32
    f1 = 12
 
    f2 := 12.0 // 如果不加小数点,f2将被推导成整型而非浮点型
 

因为浮点数不是一种精确的表达方式,所以不能像整型那样直接用==来判断两个浮点数是否相等,这可能会导致不稳定的结果。
下面是一种推荐的替代方案:

1
2
3
4
func IsEqual(f1, f2, p float64) bool {
    return math.Abs(f1-f2) < p
}
 

4、复数类型
复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部real,一个表示虚部imag。

1
2
3
4
5
6
var c1 complex64
c1 = 3.2 + 12i
 
c2 := 3.2 + 12i         // 类型为complex128
c3 := complex(3.2. 12)  // 同c2结果一致
 

5、字符串

1
2
3
4
5
6
    var str string
    str = "Hello World"
    ch := str[0]
    fmt.Printf("The length of \"%s\" is %d . \n", str, len(str))
    fmt.Printf("The first character of \"%s\" is %c, type is %T . \n", str, ch, ch)
 

Go支持两种方式遍历字符串,一种是以字节数组的方式遍历:

1
2
3
4
5
6
7
    str := "Hello, 世界"
    n := len(str)
    for i := 0; i < n; i++ {
        ch := str[i]
        fmt.Println(i, ch)
    }
 

执行此程序后发现,字符串长度为13,是因为中个字符在UTF-8中占了3个字节。

一种是以Unicode字符遍历:

1
2
3
4
5
    str := "Hello, 世界"
    for i, ch := range str {
        fmt.Println(i, ch) // ch的类型为rune
    }
 

以Unicode方式遍历时,每个字符的类型是rune。

6、字符类型
Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另个是rune,代表单个Unicode字符。

关于rune相关操作, 可查阅Go标准库的unicode包。另外unicode/utf8包也提供了utf8和unicode之间的转换。

7、数组
以下为一些常规声明数组的方式:

1
2
3
4
5
6
    [32]byte                    // 长度为32的数组,每个元素为1个字节
    [2*N] struct {x, y int32}   // 复杂类型数组
    [1000]*float64              // 指针数组
    [3][5]int                   // 二位数组
    [2][2][2]float64            // 等同于[2]([2]([2]float64))
 

元素遍历

1
2
3
4
5
6
7
8
9
10
// 方法一
for i := 0; i < len(array); i++ {
    ...
}
 
// 方法二
for i, v := range array {
    ...
}
 

需要注意的是,在Go语言中数组是一个值类型value type。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。

8、切片
数组的长度定义后就无法修改,每次赋值都将产生一份副本。为了弥补数组的不足Go语言提供了切片这种数据类型。

初看起来,切片就像指向数组的指针,实际上他拥有自己的数据结构,二不仅仅是一个指针。切片的数据结构可以抽象成以下3个变量:

  • 一个指向原生数组的指针
  • 数组切片中的元素个数
  • 切片已分配的存储空间

创建切片的方法主要有两种——基于数组和直接创建。

  • 基于数组

1
2
3
4
5
6
7
8
9
10
    // 创建数组
    var arr [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    // 基于数组创建切片
    var sli []int = arr[:5]  // 基于arr的前5个元素创建切片
    fmt.Println(cap(sli))    // 容量为10
    fmt.Println(len(sli))    // 长度为5
 
    // var sli []int = arr[:]    // 基于arr的所有元素创建切片
    // var sli []int = arr[5:]   // 基于arr的后5个元素创建切片
 

  • 直接创建

1
2
3
4
sli := make([]int, 5) // 创建一个初始元素个数为5的切片,初始值都为0
sli := make([]int, 5, 10) // 创建一个初始元素个数为5的切片,初始值都为0,并且预留了10个容量
sli := []int{1, 2, 3, 4, 5} // 直接创建并初始化包含5个元素的切片
 

元素遍历同数组一样

1
2
3
4
5
6
7
8
9
10
// 方法一
for i := 0; i < len(sli); i++ {
    ...
}
 
// 方法二
for i, v := range sli {
    ...
}
 

append和copy

1
2
3
4
5
6
7
8
9
10
11
12
    // append
    sli := make([]int, 5, 10)  // len = 5 cap = 10 sli = [0 0 0 0 0]
    sli = append(sli, 1, 2, 3) // len = 8 cap = 10 sli = [0 0 0 0 0 1 2 3]
    sli_another := []int{8, 9, 10}
    sli = append(sli, sli_another...)      // len = 11 cap = 20 sli = [0 0 0 0 0 1 2 3 8 9 10]
 
    // copy
    sli_a := []int{1, 2, 3, 4, 5}
    sli_b := []int{5, 4, 3}
    copy(sli_a, sli_b) // 只会复制sli_b的三个元素到sli_a的前三个位置中 sli_a = [5 4 3 4 5]
    copy(sli_b, sli_a) // 只会复制sli_a的前三个元素到sli_b中 sli_b = [1 2 3]
 

9、map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main
 
import (
    "fmt"
)
 
func main() {
    // 创建map
    var personDB map[string]PersonInfo
    personDB = make(map[string]PersonInfo)
 
    // 元素赋值
    personDB["1"] = PersonInfo{"1", "Tom", "Room 101..."}
    personDB["2"] = PersonInfo{"2", "Jack", "Room 102..."}
    personDB["3"] = PersonInfo{"3", "Peter", "Room 103..."}
    personDB["4"] = PersonInfo{"4", "Mike", "Room 104..."}
 
    // 元素删除
    delete(personDB, "4")
 
    // 元素查询
    person, ok := personDB["1"]
    if ok {
        fmt.Println("Found Person", person.Name, "with id ", person.ID)
    } else {
        fmt.Println("Did not find")
    }
}
 
type PersonInfo struct {
    ID      string
    Name    string
    Address string
}
 

Go语言编程 · golang

Copyright © 2022 老陈是一个普通的文艺二逼青年. 沪ICP备13044041号-1