Eli's Blog

1. Go语言相关知识点

1.1 Go特点:

  • 类型安全和内存安全
  • 以非常直观和极低的代价实现高并发
  • 高效的垃圾回收机制
  • 快速编译
  • 为多核计算机提供提升性能的方案
  • 默认UTF-8编码

1.2 GOPATH下的三个目录:

  • bin
  • pkg
  • src

1.3 相关命令:

1
2
3
4
5
6
7
8
9
10
go run    // 直接运行,调试
go build // 编译并生成可执行文件
go fmt // 格式化代码
go install // 先编译包文件,后编译整个程序
go test // 运行测试文件 (xxx_test.go测试程序xxx.go)
go doc // 查看文档

godoc fmt Println // 查看具体的函数说明

godoc --http=:8080

1.4 包导入别名

1
2
3
4
import "fmt"
import . "fmt" // 省略调用
import std "fmt" // 别名
import _ "xxx" // 只执行xxx包中的init()函数

1.5 可见性

1
2
func getField()    // private
func Find() // public

2. 字符串格式化

2.1 占位符

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
%v        默认格式
%+v 打印结构体时,添加字段名
%#v 使用Go语法表示
%T 对象类型
%% %

%t true 或 false

%b binary
%c Unicode # Printf("%c", 0x4E2D) // 中
%d decimal
%o octal
%x hex
%X HEX
%#x add prefix "0x" # %#o add "0" prefix

%s string
%q qutation

%f
%.2f
%e 科学计数法
%E
%g 科学计数法,更紧凑的,无末尾的0
%G

%p 指针地址

2.2 键盘输入

1
2
3
4
5
6
7
8
9
func main() {
var name string
var age int8

fmt.Scanln(&name)
fmt.Scanf("%d", &age)

fmt.Println(name, age)
}

2.3 字符串输入

1
2
3
4
5
6
7
8
9
10
func main() {
str := "Tom 23"

var name string
var age byte

fmt.Sscanf(str, "%s %d", &name, &age)

fmt.Println(name, age)
}

3. 位运算

3.1 原码、反码和补码:(有符号整数)

  • 二进制最高位:0-正数 1-负数

  • 正数(1):原码、反码和补码一样

    • 原码:0000 0001
    • 反码:0000 0001
    • 补码:0000 0001
  • 负数(-1):反码 —> 符号位不变,其他位取反;补码 —> 反码 + 1

    • 原码:1000 0001
    • 反码:1111 1110
    • 补码:1111 1111
  • 零:反码和补码都是0

  • 计算机运算以“补码”方式进行 (没有减法,只有加法)

    • 1 + 1:0000 0001 + 0000 0001 = 0000 0010
    • 1 + -1:0000 0001 + 1111 1111 = 1 0000 0000

3.2 位移运算

  • 右移(>>): 符号位不变,低位溢出,并用符号位补溢出的高位
  • 左移(<<): 符号位不变,低位补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
func main() {
a := 1 >> 2 // 0
b := 1 << 2 // 4
fmt.Println(a, b)

/*
1 >> 2 => 0 000 0001 -> 0 000 0000 = 0
1 << 2 => 0 000 0001 -> 0 000 0100 = 4
*/

c := -1 >> 2 // -1
d := -1 << 2 // -4
fmt.Println(c, d)

/*
-1 >> 2 =>
1 000 0001 -> 1 111 1110 -> 1 111 1111 =>
1 111 1111 -> 1 111 1110 -> 1 000 0001 = -1

-1 << 2 =>
1 000 0001 -> 1 111 1110 -> 1 111 1111 =>
1 111 1100 -> 1 111 1011 -> 1 000 0100 = -4
*/
}

3.3 Go中特殊位运算符 &^

1
2
var a int = 6 &^ 11   // 4
6 & (^11) => 6 & 4

3.4 负数位运算

1
2
3
4
5
6
7
8
9
10
11
var a byte = 2
var b byte = -2
var c byte = a ^ b // -4

/*
2补码:0000 0010
-2补码:1000 0010 -> 1111 1101 -> 1111 1110

2 ^ -2 = 0000 0010 ^ 1111 1110
= 1111 1100 -> 1111 1011 -> 1000 0100 = -4
*/

4. 获取变量占用的字节数

unsafe.Sizeof(obj)

1
2
3
4
func main() {
var n = 100
fmt.Println(unsafe.Sizeof(n))
}

5. 控制语句

5.1 for循环

1
2
3
4
5
6
7
8
9
10
11
for {

}

for a <= 3 {

}

for i := 1; i < 10; i++ {

}

5.2 switch选择

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
func main() {
var a = 1

switch a {
case 0:
fmt.Println("a=0")
case 1:
fmt.Println("a=1")
default:
fmt.Println("No Found")
}
}

// 不能写成 switch a
func main() {
var a = 1

// expression is omitted
switch {
case a >= 0:
fmt.Println("a>=0")
fallthrough
case a >= 1:
fmt.Println("a>=1")
default:
fmt.Println("No Found")
}
}

// a的作用域只在switch中
switch a := 1; {

}

// type-switch
func main() {
var x interface{} = 10

switch x.(type) {
case nil:
fmt.Println("NULL")
case int:
fmt.Println("int")
default:
fmt.Println("interface{}")
}
}

5.3 for循环示例:打印空心金字塔

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
/*
*
* *
* *
* *
*********
*/

func main() {
N := 5

// Step 1: 打印一个n*n列的正方形
for i := 1; i <= N; i++ {
// Step 3: 打印等边三角形。每行前补空格:Ln=0, Ln-1=1, L2=n-2, L1=n-1
for k := 1; k <= N-i; k++ {
fmt.Print(" ")
}

// Step 2: 打印一个直角三角形。金字塔每行*个数:L1=1,L2=3, L3=5, Ln=2n-1
for j := 1; j <= 2*i-1; j++ {
// Step 4: 空心。每行只保留第一个和最后一个
if j == 1 || j == 2*i-1 {
fmt.Print("*")
} else {
// Step 5: 最后一行全部保留
if i == N {
fmt.Print("*")
} else {
fmt.Print(" ")
}
}
}
fmt.Println()
}
}

5.4 for循环示例:九九乘法表

1
2
3
4
5
6
7
8
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d * %d = %d\t", j, i, i*j)
}
fmt.Println()
}
}

5.5 for循环示例:水仙花数

水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身(例如:1^3 + 5^3+ 3^3 = 153)

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
/*
三位的水仙花数共有4个:153,370,371,407;
四位的四叶玫瑰数共有3个:1634,8208,9474;
*/

func main() {
var N int64 = 1000000

var i int64
for i = 100; i <= N; i++ {
if isNarcissusFew(i) {
fmt.Println(i)
}
}
}

func isNarcissusFew(n int64) bool {
s := strconv.FormatInt(n, 10)
l := len(s)
var sum int64 = 0
for j := n; ; {
m := j % 10

sum += int64(math.Pow(float64(m), float64(l)))

j = j / 10
if j == 0 {
break
}
}

return sum == n
}

func isNarcissistic(n int64) bool {
s := strconv.FormatInt(n, 10)
l := len(s)

var sum int64 = 0
for _, c := range s {
num, _ := strconv.Atoi(fmt.Sprintf("%c", c))
sum += int64(math.Pow(float64(num), float64(l)))
}

return sum == n
}

性能对比:

1
2
3
4
5
6
7
8
go test -bench=. -run=none
goos: darwin
goarch: amd64
pkg: gomod/aaa
BenchmarkIsNarcissistic-4 1 93848583244 ns/op
BenchmarkIsNarcissusFew-4 1 31639945040 ns/op
PASS
ok gomod/aaa 125.791s

5.6 goto, break, continue LABEL用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
LABEL1:
for {
for i := 0; i < 10; i++ {
if i > 3 {
// break LABEL1
goto LABEL2
}
}
}

LABEL2:
fmt.Println("OK")
}

6. 随机数

1
2
3
4
5
func main() {
rand.Seed(time.Now().UnixNano()) // 种子变化越大,随机性越好
n := rand.Intn(100) + 1
fmt.Println(n)
}

7. 时间和日期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
now := time.Now() // time.Time

year := now.Year()
month := int(now.Month())
day := now.Day()
fmt.Printf("%04d-%02d-%02d\n", year, month, day)

weekday := now.Weekday()
fmt.Println(weekday)

ts := now.Unix() // timestamp
nano := now.UnixNano()
fmt.Println(ts, nano)

dateStr := now.Format("2006-01-02 15:04:05")
fmt.Println(dateStr)
}

time.Sleep(100 * time.MilliSecond)

8. 内置函数

1
2
3
4
5
6
7
8
9
10
len   // string, array, slice, map, channel
close // channel

new // 分配内存, 值类型 int, float, struct等,返回指针
make // 分配内存, 引用类型 chan, map, slice等,返回类型本身,而非指针

nptr = new(int) // *int

append // 追加元素到array, slice中
panic/recover // 错误处理

9. new和make的区别

  • new(T) 返回 T 的指针 *T 并指向 T 的零值。
  • make(T) 返回的初始化的 T,只能用于 slice,map,channel。