# 基础

# 一. 变量赋值

# 默认值

  • int 变量默认为0
  • string 变量默认为""
package main

import "fmt"

func variableZeroValue() {
 var a int
 var s string
 // Println 只能跟名字
 fmt.Println(a, s)
 // Printf 可以跟格式
 fmt.Printf("%d %q\n", a, s) // 0, ""
}

func main() {
 variableZeroValue()
}

# 变量赋值

package main

import "fmt"

func variableInitialValue() {
 var a, b int = 3, 4
 var s string = "abc"
 fmt.Println(a, b, s)
}

func main() {
 variableInitialValue()
}

# 简略写法

package main

import "fmt"

// 外部声明必须使用 var
var aa = 1
var (
 bb = "bb"
 cc = "cc"
)

func variableShortValue() {
 // 只能在 func 里面使用 := , 在函数外面必须使用 var 声明
 a, b int := 3, 4
 b = 5;
 fmt.Println(a, b)
}

func main() {
 variableShortValue()
}

# 枚举类型

package main

import "fmt"

func enums() {
 // iota 表示自增, 从1开始自增
 // 下划线表示跳过
 const (
  cpp = iota
  _
  java
  python
  golang
 )
 fmt.Println(cpp, java, python, golang)
}

func main() {
 enums()
}

# 二. 基本数据类型

  1. bool
  2. string
  3. int int8 int16 int32 int64
  4. uint uint8 uint32 uint64 uintptr(指针) (无符号)
  5. byte 字符型长度8
  6. rune 字符型长度32
  7. float32 float64
  8. complex64 complex128 (复数)

提示

没有 char 类型,只有 rune

# 三. 类型转化

  1. Go 语言不允许隐式类型转换
  2. 别名和原有类型也不能进行隐式类型转换

例如:

package try_test

import (
 "testing"
)

type MyInt int64

func TestImplicit(t *testing.T) {
 var a int32 = 1
 var b int64
 b = int64(a)
 var c MyInt
 c = b //这种也是不可以的, 必须显示声明
 c = Myint(b) //这种是可以的
}

# 四. 类型的预定义值

# 不支持指针运算

func TestPoint(t *testing.T) {
 a := 1
 aPtr := &a
 aPtr = aPtr + 1 //go不允许指针运算
 t.Logf("%T %T", a, aPtr) // int, *int
}

# string 是值类型, 其默认的初始化值为空字符串, 而不是 nil

# 五. 算数运算符

  1. + 相加
  2. - 相减
  3. * 相乘
  4. / 相除
  5. % 求余
  6. ++ 自增
  7. -- 自减

# 六. 比较运算符

  1. == 检测两个值是否相等
  2. != 检测两个值是否不相等
  3. > 大于
  4. < 小于
  5. >= 大于等于
  6. <= 小于等于

数组比较

数组比较跟 JavaScript 不一样, go 数组会对数组内部元素进行比较, 如果内部元素相等, 就认为相等

# 七. 逻辑运算符

  1. &&
  2. ||
  3. !

# 八. 位运算符

  1. &
  2. |
  3. ^
  4. <<
  5. >>

# 九. 循环

# 仅支持 for 关键字

// 循环
// for 初始值; 终止条件; 递归条件
for i := 0; i <= 9; i++ {
 ...
}

# 无限循环

// 没有 while(true) {} 关键字
for {
 ...
}

示例:进制转换

package main

import (
 "strconv"
 "fmt"
)

func convertToBin(n int) string {
 result := ""
 for ; n > 0; n /= 2 {
  lsb := n % 2
  // strconv.Iota: 转成字符串
  result = strconv.Itoa(lsb) + result
 }
 return result
}

func main() {
 fmt.Println(
  convertToBin(5),
  convertToBin(10),
 )
}

# 十. 判断

  1. condition 表达式结果必须为布尔值
  2. 支持变量赋值:
// 支持多段, 第一个语句是赋值, 第二个语句是条件
if var declaration; condition {
 // code...
}

例如:读取 abc.txt 文件,如果失败则打印失败信息。如果成功打印内容

// 业务场景
// 通过执行某个函数对变量赋值, 如果出错走到else
package main

import (
 "fmt"
 "io/ioutil"
)

func main() {
 const filename = "abc.txt"
 if contents, err := ioutil.ReadFile(filename); err != nil {
  fmt.Println(err)
 } else {
  fmt.Printf("%s\n", contents)
 }
}

# 十一. switch 条件

  1. 条件表达式不限制为常量或者整数;
  2. 单个 case 中, 可以出现多个结果选项, 使用逗号分隔;
  3. 与 C 语言等规则相反, Go 语言不需要用 break 来明确退出一个 case;
  4. 可以不设定 switch 之后的条件表达式, 在这种情况下, 整个 switch 结构与多个 if...else...的逻辑作用等同
// 相当于if else这种也是可以的
switch {
 case 0 <= Num && Num <= 3:
  fmt.Printf("0-3")
  // break 默认有break会执行
 case 4 <= Num && Num <= 6:
  fmt.Printf("4-6")
 case 7 <= Num && Num <= 9:
  fmt.Printf("7-9")
}

// case多个值, 这种也是可以的
switch i {
 case 0, 2:
  t.Log("case 0 or 2")
  // break 默认有break会执行
 default:
  t.Log("default")
}

# 十二. 数组

# 数组的声明

// 声明一个数组, 内部是3个int类型的元素
var arr [3]int

// 或者
arr := [3]int{1, 2, 3, 4}

// 也可以不显示的声明个数
arr := [...]int{1, 3, 4, 5}

// 多维数组
var grid [4][5]int

# 数组的遍历

  • (1) 常规 for
for i:=0; i<len(arr3); i++ {

}
  • (2) range
for idx, v := range arr {
 ...
}

// 如果不需要idx, 要用 "_" 占位
for _, v := range arr {
 ...
}

# 截取

  • a[开始索引(包含), 结束索引(不包含)]
func TestArraySection(t *testing.T) {
 a := [...]int{1, 2, 3, 4, 5}
 t.Log(a[1:2])     // 2
 t.Log(a[1:])     // 2, 3, 4, 5
 t.Log(a[1:len(a)])  // 2, 3, 4, 5
 t.Log(a[:3])     // 1, 2, 3
 t.Log(a[:])     // 1, 2, 3, 4, 5
}

WARNING

go 不支持负数索引, 例如 a[-1:] b[0:-1]都不可以

# 切片(slice)

(1) 声明

// 切片不声明长度
var s0 []int

(2) 添加元素

s0 = append(s0, 1);

(3) 初始化

s1 := []int{1, 2, 3, 4}

(4) 使用 make 声明切片

  • make(切片, 可访问长度, 容量)
s2 := make([]int, 3, 5)

# 切片内部结构

  • 指针 ptr 指向数组
  • len: 元素的个数
  • cap: 内部数组的容量

WARNING

(1) 容量随着 append 填充元素可动态伸缩, 并不是跟长度一致, 而是指数级伸缩, 长度扩展量为 1,2,4,8,16...

(2) 不可以进行比较

// 下面的示例会报错, 两个切片是不允许比较的
func TestSliceComparing(t *testing.T) {
 a := []int{1, 2, 3, 4}
 b := []int{1, 2, 3, 4}
 if a == b {
  t.Log("equal")
 }
}