Jinyun's Notes

没什么天赋,爱好也不多,但愿坚持做些喜欢的事情

0%

Go 数据类型

🦋 Go 是静态类型的编程语言,这意味着每个变量都有一种类型。从大的方面来说 Go 有两种类型,即基本类型和组合类型。数据类型指定有效的 Go 变量可以保持的数据类型。

按基本数据类型分类

基本类型

布尔型

bool 的零值为 false。

仅赋值为 true 和 false 来表示真假。Go 没有定义布尔类型的字面量形式,可以将 true 和 false 这两个预声明的有名常量当作布尔类型的字面量形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"reflect"
"unsafe"
)

var b1 bool

func main() {
b2 := true
fmt.Printf("b1: %s type, %d bytes\n", reflect.TypeOf(b1), unsafe.Sizeof(b1))
fmt.Printf("b2: %s type, %d bytes\n\n", reflect.TypeOf(b2), unsafe.Sizeof(b2))
}

/*
b1: bool type, 1 bytes
b2: bool type, 1 bytes
*/

数值类型

numeric 的零值为 0(但是不同类型的零值在内存中占用的空间可能不同)。

整数型

包括有符号(Signed)和无符号(Unsigned)类型。

TypeSizeRangeDescription
int81-128~127 or -2^7~2^7-1有符号 8 位整形
uint810~255 or 2^8无符号 8 位整形
int162-32768~32767 or -2^15~2^15-1有符号 16 位整形
uint1620~65535 or 2^16无符号 16 位整形
int324-2147483648~2147483647 or -2^31~2^31-1有符号 32 位整形
uint3240~4294967295 or 2^31无符号 32 位整形
int648-9223372036854775808~9223372036854775807 or -2^63~2^63-1有符号 64 位整形
uint6480~18446744073709551615 or 0~2^64无符号 64 位整形
int4 or 8Platform dependent有符号 32 或 64 位整形
uint4 or 8Platform dependent无符号 32 或 64 位整形
uintptr4 or 8Platform dependent用于存放一个指针

uintptr 特性:

  • 可以被转换为 unsafe.Pointer,反之亦然;
  • 可以在其上执行算术运算;
  • 即使包含指针地址,也只是一个值,也不引用任何对象。所以:
    • 如果相应对象移动,则不会更新其值。例如:当 Goroutine 堆栈发生变化时;
    • 相应的对象可以是被收集的垃圾;

uintptr 场景:

  • 其上的是与 unsafe.Pointer 一起主要用于不安全的内存访问;
  • 当你想要保存指针地址值以进行打印或存储它。由于地址刚刚存储并且不引用任何内容,因此相应的对象可以是垃圾收集的;
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package main

import (
"fmt"
"math/bits"
"reflect"
"unsafe"
)

var (
i8 int8
u8 uint8

i16 int16
u16 uint16

i32 int32
u32 uint32

i64 int64
u64 uint64

i int
u uint
)

type student struct {
name string
age int
}

func main() {
fmt.Printf("i8: %d bits, %s type, %d bytes\n", bits.UintSize, reflect.TypeOf(i8), unsafe.Sizeof(i8))
fmt.Printf("u8: %d bits, %s type, %d bytes\n\n", bits.UintSize, reflect.TypeOf(u8), unsafe.Sizeof(u8))

fmt.Printf("i16: %d bits, %s type, %d bytes\n", bits.UintSize, reflect.TypeOf(i16), unsafe.Sizeof(i16))
fmt.Printf("u16: %d bits, %s type, %d bytes\n\n", bits.UintSize, reflect.TypeOf(u16), unsafe.Sizeof(u16))

fmt.Printf("i32: %d bits, %s type, %d bytes\n", bits.UintSize, reflect.TypeOf(i32), unsafe.Sizeof(i32))
fmt.Printf("u32: %d bits, %s type, %d bytes\n\n", bits.UintSize, reflect.TypeOf(u32), unsafe.Sizeof(u32))

fmt.Printf("i64: %d bits, %s type, %d bytes\n", bits.UintSize, reflect.TypeOf(i64), unsafe.Sizeof(i64))
fmt.Printf("u64: %d bits, %s type, %d bytes\n\n", bits.UintSize, reflect.TypeOf(u64), unsafe.Sizeof(u64))

fmt.Printf("i: %d bits, %s type, %d bytes\n", bits.UintSize, reflect.TypeOf(i), unsafe.Sizeof(i))
fmt.Printf("u: %d bits, %s type, %d bytes\n\n", bits.UintSize, reflect.TypeOf(u), unsafe.Sizeof(u))

s := &student{name: "tony", age: 18}

// Get the address of field name in struct s
p := unsafe.Pointer(uintptr(unsafe.Pointer(s)) + unsafe.Offsetof(s.name))

// Typecast it to a string pointer and print the value of it
fmt.Printf("p: %s", *(*string)(p))
}

/*
i8: 64 bits, int8 type, 1 bytes
u8: 64 bits, uint8 type, 1 bytes

i16: 64 bits, int16 type, 2 bytes
u16: 64 bits, uint16 type, 2 bytes

i32: 64 bits, int32 type, 4 bytes
u32: 64 bits, uint32 type, 4 bytes

i64: 64 bits, int64 type, 8 bytes
u64: 64 bits, uint64 type, 8 bytes

i: 64 bits, int type, 8 bytes
u: 64 bits, uint type, 8 bytes

p: tony
*/

byte 为 uint8 的别名。因此它是一个整数值,此整数值为 8 位,它表示 0~255 之间的一个字节。单个字节可以代表 ASCII 字符。Go 没有任何 char 数据类型,因此字节用于表示 ASCII 字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"fmt"
"reflect"
"unsafe"
)

func main() {
var char byte = 'a'

fmt.Printf("Type: %s, Size: %d\n", reflect.TypeOf(char), unsafe.Sizeof(char))
fmt.Printf("Character: %c\n", char)

s := "abc"
fmt.Println([]byte(s))
}

/*
Type: uint8, Size: 1
Character: a
[97 98 99]
*/

rune 为 int32 的别名。因此它是一个整数值,此整数值旨在表示 Unicode Code Point。关于 Unicode 和字符集请戳 The Absolute Minimum Every Software Developer Absolutely, Positively. Must Know About Unicode and Character Sets(No Excuses!)。

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
package main

import (
"fmt"
"reflect"
"unsafe"
)

func main() {
char := 'a'

fmt.Printf("Type: %s, Size: %d\n", reflect.TypeOf(char), unsafe.Sizeof(char))
fmt.Printf("Character: %c\n", char)
fmt.Printf("Unicode Code Point: %U\n", char)

s := "0b™"

// This will print the Unicode Code Point
fmt.Printf("%U\n", []rune(s))

// This will the decimal value of Unicode Code Point
fmt.Println([]rune(s))
}

/*
Type: int32, Size: 4
Character: a
Unicode Code Point: U+0061
[U+0030 U+0062 U+2122]
[48 98 8482]
*/

整数类型有四种字面量形式:十进制形式(Decimal)、八进制形式(Octal)、十六进制形式(Hex)和二进制形式(Binary);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 十六进制表示
0xF
0XF

// 八进制表示(0o 或 0O 开头的八进制形式从 Go 1.13 才开始支持)
017
0o17
0O17

// 二进制表示
0b1011
0B1011

// 十进制表示
32
浮点型

在内存中,所有的浮点数都是使用 IEEE-754 格式存储的。

TypeSizeRangeDescription
float324正负1.18*10^-38~正负3.40*10^3832 位的浮点型
float648正负2.23*10^-308~正负1.80*10^30864 位的浮点型
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
package main

import (
"fmt"
"reflect"
"unsafe"
)

var f32 float32
var f64 float64

func main() {
var f1 float32
f1 = 3.1415926
f2 := 3.1415926 // Default is float64
fmt.Printf("f32: %s type, %d bytes\n", reflect.TypeOf(f32), unsafe.Sizeof(f32))
fmt.Printf("f64: %s type, %d bytes\n\n", reflect.TypeOf(f64), unsafe.Sizeof(f64))
fmt.Printf("f1: %s type, %d bytes\n", reflect.TypeOf(f1), unsafe.Sizeof(f1))
fmt.Printf("f2: %s type, %d bytes\n\n", reflect.TypeOf(f2), unsafe.Sizeof(f2))
}

/*
f32: float32 type, 4 bytes
f64: float64 type, 8 bytes

f1: float32 type, 4 bytes
f2: float64 type, 8 bytes
*/

浮点数类型值的字面量形式:一个浮点数的完整十进制字面量形式可能包含一个十进制整数部分、一个小数点、一个十进制小数部分和一个以 10 为底数的整数指数部分。整数指数部分字母 e 或 E 带一个十进制的整数字面量组成(xEn 表示 x 乘以 10^n,而 xE-n 表示 x 除以 10^n)。

1
2
3
4
5
6
7
8
9
10
11
12
10.1234
03.1415 // 3.1415
.88
99.

1.23e2 // 123.0
123E2 // 12300.0
123.E+2 // 12300.0
1e-1 // 0.1
.1e0 // 0.1
0010e-2 // 0.1
0e+5 // 0.0

一些合法的浮点数的十六进制字面量:

1
2
3
4
5
0x1p-2     // == 1.0/4 = 0.25
0x2.p10 // == 2.0 * 1024 == 2048.0
0x1.Fp+0 // == 1+15.0/16 == 1.9375
0X.8p1 // == 8.0/16 * 2 == 1.0
0X1FFFP-16 // == 0.1249847412109375
复数型
TypeSizeRangeDescription
complex648N/A64 位的复数型,由 float32 位的实部和虚部联合表示
complex12816N/A128 位的复数型,由 float64 位的实部和虚部联合表示
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
36
37
38
39
40
41
42
package main

import (
"fmt"
"reflect"
"unsafe"
)

func main() {
var a float32 = 3
var b float32 = 5

var m float64 = 3
var n float64 = 5

c := complex(a, b)
var d complex64
d = 4 + 5i

p := complex(m, n)
q := 4 + 5i // When don't specify a type, the default type will be complex128

fmt.Printf("c: %s type, %d bytes\n", reflect.TypeOf(c), unsafe.Sizeof(c))
fmt.Printf("d: %s type, %d bytes\n", reflect.TypeOf(d), unsafe.Sizeof(d))
fmt.Println(c+d, c-d, c*d, c/d)

fmt.Println()

fmt.Printf("p: %s type, %d bytes\n", reflect.TypeOf(p), unsafe.Sizeof(p))
fmt.Printf("q: %s type, %d bytes\n", reflect.TypeOf(q), unsafe.Sizeof(q))
fmt.Println(p+q, p-q, p*q, p/q)
}

/*
c: complex64 type, 8 bytes
d: complex64 type, 8 bytes
(7+10i) (-1+0i) (-13+35i) (0.902439+0.12195122i)

p: complex128 type, 16 bytes
q: complex128 type, 16 bytes
(7+10i) (-1+0i) (-13+35i) (0.902439024390244+0.12195121951219513i)
*/

复数虚部字面量形式:一个虚部值的字面量形式由一个浮点数字面量或者一个整数字面量和其后跟随的一个小写的字母 i 组成。 在 Go 1.13 之前,如果虚部中 i 前的部分为一个整数字面量,则其必须为并且总是被视为十进制形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.23i
1.i
.23i
123i
0123i // == 123i(兼容性使然。见下)
1.23E2i // == 123i
1e-1i
011i // == 11i(兼容性使然。见下)
00011i // == 11i(兼容性使然。见下)

// 下面这几行从 Go 1.13 开始才能编译通过
0o11i // == 9i
0x11i // == 17i
0b11i // == 3i
0X.8p-0i // == 0.5i
字符串型

string 的零值为 "",即空字符串。

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
package main

import (
"fmt"
)

func main() {
x := "hello\nworld"
fmt.Printf("x: %s\n", x)

y := `Hello\nWorld`
fmt.Printf("y: %s\n", y)

s := "¡Q™"
fmt.Printf("s: %s\n", s)
fmt.Printf("s length is: %d\n", len(s))

for _, c := range s {
fmt.Println(string(c))
}
fmt.Println("🚀" + " " + "Hello World!")
}

/*
x: hello
world
y: Hello\nWorld
s: ¡Q™
s length is: 6
¡
Q
™
🚀 Hello World!
*/

Go 字符串的字面量形式有两种:一种是解释型字面表示(Interpreted String Literal,双引号风格)。 另一种是直白字面表示(Raw String Literal,反引号风格)。

1
2
3
4
5
// 解释形式
"Hello World!\n"

// 直白形式
`Hello World!`

组合类型

非引用类型

数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
)

func main() {
var arr1 [2]string
arr1[0] = "a"
arr1[1] = "b"
dump("arr1", arr1)

arr2 := [2]string{"Hello", "World"}
dump("arr2", arr2)
}

func dump(name string, array [2]string) {
fmt.Printf("%s: %v\n", name, array)
}

/*
arr1: [a b]
arr2: [Hello World]
*/
结构体
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
package main

import (
"fmt"
)

type employee struct {
name string
age int
salary float64
}

func main() {
e1 := employee{"John", 28, 12000.00}
fmt.Printf("e1: %v\n", e1)

e2 := employee{name: "Jack", age: 26, salary: 8000.00}
fmt.Printf("e2: %v\n", e2)

e3 := employee{name: "Pony", age: 27}
fmt.Printf("e3: %v\n", e3)
}

/*
e1: {John 28 12000}
e2: {Jack 26 8000}
e3: {Pony 27 0}
*/

引用类型

切片

切片是动态大小的,数组是固定大小的,因此切片提供更灵活的接口。切片是引用类型,因为它在内部引用数组。它的内部表示通过三个字段:

  • 底层数组的地址;
  • 切片的长度;
  • 切片的容量;
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
package main

import (
"fmt"
"reflect"
)

func main() {
s := make([]string, 2, 3)
fmt.Printf("Slice: %v, Type: %s, Len: %d, Cap: %d\n", s, reflect.TypeOf(s), len(s), cap(s))

p := []string{"a", "b", "c"}
fmt.Printf("Slice: %v, Type: %s, Len: %d, Cap: %d\n", p, reflect.TypeOf(p), len(p), cap(s))

p = append(p, "d")
fmt.Printf("Slice: %v, Type: %s, Len: %d, Cap: %d\n", p, reflect.TypeOf(p), len(p), cap(s))

for _, v := range p {
fmt.Println(v)
}
}

/*
Slice: [ ], Type: []string, Len: 2, Cap: 3
Slice: [a b c], Type: []string, Len: 3, Cap: 3
Slice: [a b c d], Type: []string, Len: 4, Cap: 3
a
b
c
d
*/
映射

map 的零值是 nil。

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
package main

import (
"fmt"
"reflect"
)

func main() {
var student map[string]interface{}
fmt.Printf("Type: %s, Value: %v\n", reflect.TypeOf(student), student)

stu1 := make(map[string]int)
fmt.Printf("Type: %s, Value: %v\n", reflect.TypeOf(stu1), stu1)

stu2 := map[string]int{"John": 18, "Jack": 20}
stu2["Carl"] = 28

fmt.Printf("John age is %d\n", stu2["John"])
delete(stu2, "Carl")

fmt.Printf("\nPrint stu2 map:")
fmt.Println(stu2)
}

/*
Type: map[string]interface {}, Value: map[]
Type: map[string]int, Value: map[]
John age is 18

Print stu2 map:map[Jack:20 John:18]
*/
通道

通道提供 Goroutine 之间的同步和通信。你可以将其视为管道,Goroutine 可以发送值和接收值。操作 <- 用于发送或接收,并指定数据流动方向的箭头方向。

两种类型的通道:

  • 无缓冲通道(Unbuffered Channel):它没有任何容纳能力和值:
    • 除非有另一个 Goroutine 接收,否则发送在频道上是阻塞的;
    • 接收是阻塞的,直到另一个 Goroutine 在一边去发送;
  • 带缓冲通道(Buffered Channel):可以在此处指定缓冲区的大小:
    • 如果缓冲区已满,则仅发送阻塞的缓冲通道;
    • 接收唯一的阻塞是通道的缓冲区是空的;
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
36
37
38
39
package main

import (
"fmt"
)

func main() {
// Creating a buffered channel of length 4
bufChan := make(chan string, 4)
bufChan <- "🚀 Hello "
bufChan <- "World, "
bufChan <- "Hello "
bufChan <- "Go!\n"

close(bufChan)
for ch := range bufChan {
fmt.Print(ch)
}

// Creating a unbuffered channel
unbufChan := make(chan string)
go sendChan(unbufChan)
for ch := range unbufChan {
fmt.Print(ch)
}
}

func sendChan(ch chan<- string) {
ch <- "🚀 Hello "
ch <- "World, "
ch <- "Hello "
ch <- "Go!"
close(ch)
}

/*
🚀 Hello World, Hello Go!
🚀 Hello World, Hello Go!
*/
函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
add := func(x, y int) int {
return x + y
}
fmt.Println(doExecute(add, 10, 20))
}

func doExecute(fn func(int, int) int, x, y int) int {
return fn(x, y)
}

/*
30
*/
指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
var b *int
a := 88
b = &a
fmt.Printf("b: %v, *b: %v\n", b, *b)

b = new(int)
*b = 99
fmt.Printf("b: %v, *b: %v\n", b, *b)
}

/*
b: 0xc0000ac008, *b: 88
b: 0xc0000ac018, *b: 99
*/

Interface

interface 的零值是 nil。

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
36
37
38
package main

import "fmt"

type shape interface {
area() float64
}

type square struct {
side float64
}

type triangle struct {
bottom float64
height float64
}

func (s *square) area() float64 {
return s.side * s.side
}

func (t *triangle) area() float64 {
return (t.bottom * t.height) / 2
}

func main() {
var s shape
s = &square{side: 4}
fmt.Printf("Square area is %v\n", s.area())

t := &triangle{bottom: 10, height: 12}
fmt.Printf("Triangle area is %v\n", t.area())
}

/*
Square area is 16
Triangle area is 60
*/

空接口的特殊情况:

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
package main

import "fmt"

func main() {
var a = [2]string{"go", "js"}
var m = map[int]string{1: "hello", 2: "world"}
dump("🚀 Hello World, Hello Go!")
dump(123456780)
dump(true)
dump(a)
dump(m)
}

func dump(v interface{}) {
fmt.Printf("Type: %T, Value: %v\n", v, v)
}

/*
Type: string, Value: 🚀 Hello World, Hello Go!
Type: int, Value: 123456780
Type: bool, Value: true
Type: [2]string, Value: [go js]
Type: map[int]string, Value: map[1:hello 2:world]
*/

数值类型运算符

  • 算术运算符:+、-、*、/、%
  • 比较运算符:==、!=、>、>=、<=、<
  • 逻辑运算符:&&、||、!
  • 增减运算符:++、--
  • 按位运算符:&、|、^、<<、>>、&^
  • 赋值运算符:+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=

运算符优化级

PriorityCategoryOperatorAssociativity
1Postfix() [] -> . ++ --Left to right
2Unary+ - ! ~ ++ -- (type)* & sizeofRight to left
3Multiplicative* / %Left to right
4Additive+ -Left to right
5Shift<< >>Left to right
6Relational< <= >= >Left to right
7Equality== !=Left to right
8Bitwise AND&Left to right
9Bitwise XOR^Left to right
10Bitwise OR⎮Left to right
11Logical AND&&Left to right
12Logical OR⎮⎮Left to right
13Assignment= += -= *= /= %= = >>= <<= &= ^= ⎮=Right to right
14Comma,Left to right
本笔记是笔者在学习和工作中的一些整理,如对您有用,请鼓励我继续写作