基础语法
变量
变量声明主要有三种方式:
1.使用关键字 var 来声明;
2.使用 := 的短变量声明方式;
3.使用内置函数 new。
package main
import "fmt"
// 全局变量
var gg = "global"
func main() {
// 作用域
fmt.Println(gg) // 输出 global
gg = "local"
fmt.Println(gg) // 输出 local
// 第一种方式:通过var来声明
var a = "initial" // 声明单个变量
var d = true // 声明布尔值
// 以组的方式声明多个变量
var (
b1, c1 int // 默认会赋予0
b2, c2 = 3, 4
)
// 第二种方式:使用短变量声明方式
// go会自己判断类型
f := "short"
// 什么多个值
g, h := 5, "alwaysbeta"
fmt.Println(a)
fmt.Println(d)
fmt.Println(b1, c1, b2, c2)
fmt.Println(f, g, h)
// 赋值 使用=
var m, n int
m = 9
n = 10
fmt.Println(m, n) // 9, 10
// 直接交换值
m, n = n, m
fmt.Println(m, n) // 10, 9
// 如果有不需要的变量,使用空标识符 _ 来忽略
// 空标识符
r := [5]int{1, 2, 3, 4, 5}
for _, v := range r {
// fmt.Println(i, v)
// fmt.Println(v) // 定义 i 但不用会报错 i declared but not used
fmt.Println(v) // 忽略索引
}
}
常量
package main
import "fmt"
func main() {
// 无类型整型常量
const n = 500000000
// 用编译阶段即可计算出值的表达式来赋值
const d = 3e20 / n
fmt.Println(d)
// 无类型浮点常量
const zero = 0.0
// 无类型整型和字符串常量
const a, b, c = 3, 4, "foo"
fmt.Println(a, b, c)
// 多个常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
fmt.Println(size, eof)
// 常量声明还有可以使用常量生成器 iota,它不会显示写出常量的值,而是从 0 开始,逐项加 1。
// iota 在每个 const 开头被重置为 0。
// 从 0 值开始,逐项加 1
const (
a0 = iota // 0
a1 = iota // 1
a2 = iota // 2
)
fmt.Println(a0, a1, a2)
// 简写,表达式相同,可以省略后面的
const (
b0 = iota // 0
b1 // 1
b2 // 2
)
fmt.Println(b0, b1, b2)
const (
bb = iota // 0
cc float32 = iota * 10 // 10
dd = iota // 2
)
fmt.Println(bb, cc, dd)
}
基础数据类型
Go 的数据类型分四大类:
基础类型: 数字 number,字符串 string 和布尔型 boolean。
聚合类型: 数组 array 和结构体 struct。
引用类型: 指针 pointer,切片 slice,字典 map,函数 func 和通道 channel。
接口类型: 接口 interface。
其中,基础类型又分为:
整型: int8、uint8、byte、int16、uint16、int32、uint32、int64、uint64、int、uint、uintptr。
浮点型: float32,float64。
复数类型: complex64、complex128。
布尔型: bool。
字符串: string。
字符型: rune。

package main
import "fmt"
func main() {
var a int = 10
var b int32 = 20
// fmt.Println(a + b) // 报错 invalid operation: a + b (mismatched types int and int32)
// 需要强制类型转换
fmt.Println(a + int(b)) // 输出 30
// 浮点型转整型
var c float32 = 10.23
fmt.Println(int(c)) // 输出 10
// 取模
fmt.Println(5 % 3) // 输出 2
fmt.Println(-5 % -3) // 输出 -2
// 除法
fmt.Println(5 / 3) // 输出 1
fmt.Println(5.0 / 3.0) // 输出 1.6666666666666667
// 比较运算
var i int32
var j int64
i, j = 1, 2
// if i == j { // 报错 invalid operation: i == j (mismatched types int32 and int64)
// fmt.Println("i and j are equal.")
// }
if i == 1 || j == 2 {
fmt.Println("equal.")
}
// 复数
var x complex64 = 3 + 5i
var y complex128 = complex(3.5, 10)
// 分别打印实部和虚部
fmt.Println(real(x), imag(x)) // 输出 3 5
fmt.Println(real(y), imag(y)) // 输出 3.5 10
// 布尔
ok := true
fmt.Println(ok)
// 类型转换
// var e bool
// e = bool(1) // 报错 cannot convert 1 (type untyped int) to type bool
m := 1
// if m { // 报错 non-bool m (type int) used as if condition
// fmt.Println("is true")
// }
if m == 1 {
fmt.Println("m is 1")
}
// 字符串
s1 := "hello"
s2 := "world"
// 原始字符串
s := `row1\r\n
row2`
fmt.Println(s)
// 字符串拼接
s3 := s1 + s2
fmt.Println(s3)
// 取字符串长度
fmt.Println(len(s3))
// 取单个字符
fmt.Println(s3[4])
// 字符串切片
fmt.Println(s3[2:4])
fmt.Println(s3[:4])
fmt.Println(s3[2:])
fmt.Println(s3[:])
// 修改报错
// s3[0] = "H" // cannot assign to s3[0] (strings are immutable)
s4 := "hello 世界"
// 遍历字节数组
for i := 0; i < len(s4); i++ {
fmt.Println(i, s4[i])
}
// 遍历 rune 数组
for i, v := range s4 {
fmt.Println(i, v)
}
}
数组
数组有两个特点:
固定长度
元素类型相同
Go 数组是值类型,赋值和传参都会复制整个数组。
代码如下:
package main
import "fmt"
func main() {
var a [3]int
// 输出数组第一个元素
fmt.Println(a[0]) // 0
// 输出数组长度
fmt.Println(len(a)) // 3
// 数组字面量初始化
var b [3]int = [3]int{1, 2, 3}
var c [3]int = [3]int{1, 2}
fmt.Println(b) // [1 2 3]
fmt.Println(c[2]) // 0
// 使用 ...
d := [...]int{1, 2, 3, 4, 5}
fmt.Printf("%T\n", d) // [5]int
// 指定索引位置初始化
e := [4]int{5, 2: 10}
f := [...]int{2, 4: 6}
fmt.Println(e) // [5 0 10 0]
fmt.Println(f) // [2 0 0 0 6]
// 二维数组
var g [4][2]int
h := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化外层数组中索引为 1 和 3 的元素
i := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化外层数组和内层数组的单个元素
j := [...][2]int{1: {0: 20}, 3: {1: 41}}
// [[0 0] [0 0] [0 0] [0 0]]
// [[10 11] [20 21] [30 31] [40 41]]
// [[0 0] [20 21] [0 0] [40 41]]
// [[0 0] [20 0] [0 0] [0 41]]
fmt.Println(g, h, i, j)
// 数组比较
a1 := [2]int{1, 2}
a2 := [...]int{1, 2}
a3 := [2]int{1, 3}
// a4 := [3]int{1, 2}
fmt.Println(a1 == a2, a1 == a3, a2 == a3) // true false false
// fmt.Println(a1 == a4) // invalid operation: a1 == a4 (mismatched types [2]int and [3]int)
// 数组遍历
for i, n := range e {
fmt.Println(i, n)
}
// 0 5
// 1 0
// 2 10
// 3 0
// Go 数组是值类型,赋值和传参都会复制整个数组。
// 数组复制,内容都是相同的,但地址不同
x := [2]int{10, 20}
y := x
fmt.Printf("x: %p, %v\n", &x, x) // x: 0xc00012e020, [10 20]
fmt.Printf("y: %p, %v\n", &y, y) // y: 0xc00012e030, [10 20]
test(x)
// 传参
modify(x)
fmt.Println("main: ", x) // main: [10 20]
}
func test(a [2]int) {
fmt.Printf("a: %p, %v\n", &a, a) // a: 0xc00012e060, [10 20]
}
func modify(a [2]int) {
a[0] = 30
fmt.Println("modify: ", a) // modify: [30 20]
}
切片
切片是一种引用类型,它有三个属性:指针,长度和容量。
指针:指向 slice 可以访问到的第一个元素。
长度:slice 中元素个数。
容量:slice 起始元素到底层数组最后一个元素间的元素个数。

代码如下:
package main
import "fmt"
func main() {
// 创建切片第一种方式:基于数组创建切片
var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8}
s1 := array[3:6]
s2 := array[:5]
s3 := array[4:]
s4 := array[:]
fmt.Printf("s1: %v\n", s1) // s1: [4 5 6]
fmt.Printf("s2: %v\n", s2) // s2: [1 2 3 4 5]
fmt.Printf("s3: %v\n", s3) // s3: [5 6 7 8]
fmt.Printf("s4: %v\n", s4) // s4: [1 2 3 4 5 6 7 8]
// 创建切片第二种方式:使用 make 创建切片
// len: 10, cap: 10
a := make([]int, 10)
// len: 10, cap: 15
b := make([]int, 10, 15)
fmt.Printf("a: %v, len: %d, cap: %d\n", a, len(a), cap(a)) // a: [0 0 0 0 0 0 0 0 0 0], len: 10, cap: 10
fmt.Printf("b: %v, len: %d, cap: %d\n", b, len(b), cap(b)) // b: [0 0 0 0 0 0 0 0 0 0], len: 10, cap: 15
// 切片遍历,和遍历数组是一样的
for i, n := range s1 {
fmt.Println(i, n)
}
// 比较
var s []int
fmt.Println(len(s) == 0, s == nil) // true true
s = nil
fmt.Println(len(s) == 0, s == nil) // true true
s = []int(nil)
fmt.Println(len(s) == 0, s == nil) // true true
s = []int{}
fmt.Println(len(s) == 0, s == nil) // true false
// 追加
s5 := append(s4, 9)
fmt.Printf("s5: %v\n", s5) // s5: [1 2 3 4 5 6 7 8 9]
s6 := append(s4, 10, 11)
fmt.Printf("s6: %v\n", s6) // s5: [1 2 3 4 5 6 7 8 10 11]
// 追加另一个切片
s7 := []int{12, 13}
s7 = append(s7, s6...)
fmt.Printf("s7: %v\n", s7) // s7: [12 13 1 2 3 4 5 6 7 8 10 11]
// 复制
s8 := []int{1, 2, 3, 4, 5}
s9 := []int{5, 4, 3}
s10 := []int{6}
copy(s8, s9)
fmt.Printf("s8: %v\n", s8) // s8: [5 4 3 4 5]
copy(s10, s9)
fmt.Printf("s10: %v\n", s10) // s10: [5]
// 传参
modify(s9)
fmt.Println("main: ", s9) // main: [30 4 3]
}
// 在 modify 中修改的值会影响到 main 中。
func modify(a []int) {
a[0] = 30
fmt.Println("modify: ", a) // modify: [30 4 3]
}
字典Map
字典是一种非常常用的数据结构,Go 中用关键词 map 表示,类型是 map[K]V。K 和 V 分别是字典的键和值的数据类型,其中键必须支持相等运算符,比如数字,字符串等。
代码如下:
package main
import "fmt"
func main() {
// 字面量方式创建
var m = map[string]int{"a": 1, "b": 2}
fmt.Println(m) // map[a:1 b:2]
// 使用 make 创建
m1 := make(map[string]int)
fmt.Println(m1) // map[]
// 指定长度
m2 := make(map[string]int, 10)
fmt.Println(m2) //map[]
// 零值是 nil
var m3 map[string]int
fmt.Println(m3 == nil, len(m3) == 0) // true true
// nil 赋值报错
// m3["a"] = 1
// fmt.Println(m3) // panic: assignment to entry in nil map
// 赋值
m["c"] = 3
m["d"] = 4
fmt.Println(m) // map[a:1 b:2 c:3 d:4]
// 取值
fmt.Println(m["a"], m["d"]) // 1 4
fmt.Println(m["k"]) // 0
// 删除
delete(m, "c")
delete(m, "f") // key 不存在也不报错
fmt.Println(m) // map[a:1 b:2 d:4]
// 获取长度
fmt.Println(len(m)) // 3
// 判断键是否存在
if value, ok := m["d"]; ok {
fmt.Println(value) // 4
}
// 遍历
for k, v := range m {
fmt.Println(k, v)
}
// 传参
// map 是引用类型,所以在函数间传递时,也不会制造一个映射的副本,这点和切片类似,都很高效。
modify(m)
fmt.Println("main: ", m) // main: map[a:1 b:2 d:4 e:10]
}
func modify(a map[string]int) {
a["e"] = 10
fmt.Println("modify: ", a) // modify: map[a:1 b:2 d:4 e:10]
}
结构体Struct
结构体是一种聚合类型,包含零个或多个任意类型的命名变量,每个变量叫做结构体的成员。
代码如下:
package main
import "fmt"
// 声明结构体
type user struct {
name string
age int
}
type admin struct {
u user
isAdmin bool
}
type leader struct {
u user
isLeader bool
}
type admin1 struct {
user
isAdmin bool
}
func main() {
// 初始化
u1 := user{"zhangsan", 18}
fmt.Println(u1) // {zhangsan 18}
// 更好的方式, 按照字段名字来初始化。
// u := user{
// age: 20,
// }
// fmt.Println(u) // { 20}
u := user{
name: "zhangsan",
age: 18,
}
fmt.Println(u) // {zhangsan 18}
// 访问结构体成员
fmt.Println(u.name, u.age) // zhangsan 18
u.name = "lisi"
fmt.Println(u.name, u.age) // lisi 18
// 结构体比较
u2 := user{
age: 18,
name: "zhangsan",
}
fmt.Println(u1 == u) // false
fmt.Println(u1 == u2) // true
// 结构体嵌套
a := admin{
u: u,
isAdmin: true,
}
fmt.Println(a) // {{lisi 18} true}
a.u.name = "wangwu"
fmt.Println(a.u.name) // wangwu
fmt.Println(a.u.age) // 18
fmt.Println(a.isAdmin) // true
l := leader{
u: u,
isLeader: false,
}
fmt.Println(l) // {{lisi 18} false}
// 匿名成员, 通过这种方式可以省略掉中间变量,直接访问我们需要的成员变量。
a1 := admin1{
user: u,
isAdmin: true,
}
a1.age = 20
a1.isAdmin = false
fmt.Println(a1) // {{lisi 20} false}
fmt.Println(a1.name) // lisi
fmt.Println(a1.age) // 20
fmt.Println(a1.isAdmin) // false
}
ifelse
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd") // 7 is odd
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4") // 8 is divisible by 4
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit") // 9 has 1 digit
} else {
fmt.Println(num, "has multiple digits")
}
}
switchcase
package main
import (
"fmt"
"time"
)
func main() {
i := 2
fmt.Print("write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two") // write 2 as two
fallthrough
case 3:
fmt.Println("three") // three
case 4, 5, 6:
fmt.Println("four, five, six")
}
switch num := 9; num {
case 1:
fmt.Println("one")
default:
fmt.Println("nine") // nine
}
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("it's the weekend")
default:
fmt.Println("it's a weekday") // it's a weekday
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("it's before noon")
default:
fmt.Println("it's after noon") // it's after noon
}
}
for
直接代码:
package main
import (
"fmt"
)
func main() {
i := 1
// 只有条件
for i <= 3 {
fmt.Println(i) // 1 2 3
i = i + 1
}
// 有变量初始化和条件
for j := 7; j <= 9; j++ {
fmt.Println(j) // 7 8 9
}
// 死循环
for {
fmt.Println("loop")
break
}
// 遍历数组
a := [...]int{10, 20, 30, 40}
for i := range a {
fmt.Println(i) // 0 1 2 3
}
for i, v := range a {
fmt.Println(i, v) // 0 10, 1 20, 2 30, 3 40
}
// 遍历切片
s := []string{"a", "b", "c"}
for i := range s {
fmt.Println(i) // 0 1 2
}
for i, v := range s {
fmt.Println(i, v) // 0 a, 1 b, 2 c
}
// 遍历字典
m := map[string]int{"a": 10, "b": 20, "c": 30}
for k := range m {
fmt.Println(k) // a b c
}
for k, v := range m {
fmt.Println(k, v)
}
}
goto
goto 特点:
只能在函数内跳转,需要配合标签一起使用;
不能跳过内部变量声明语句;
只能跳到同级作用域或者上层作用域内,不能跳到内部作用域内。
直接代码
package main
import (
"fmt"
)
func main() {
// 跳出循环
for i := 0; ; i++ {
if i == 2 {
goto L1
}
fmt.Println(i)
}
L1:
fmt.Println("Done")
// 跳过变量声明,不允许
// goto L2
// j := 1
// L2:
// break 跳转到标签处,然后跳过 for 循环
L3:
for i := 0; ; i++ {
for j := 0; ; j++ {
if i >= 2 {
break L3
}
if j > 4 {
break
}
fmt.Println(i, j)
}
}
// continue 跳转到标签处,然后执行 i++
L4:
for i := 0; ; i++ {
for j := 0; j < 6; j++ {
if i > 4 {
break L4
}
if i >= 2 {
continue L4
}
if j > 4 {
continue
}
fmt.Println(i, j)
}
}
}
// 0
// 1
// Done
// 0 0
// 0 1
// 0 2
// 0 3
// 0 4
// 1 0
// 1 1
// 1 2
// 1 3
// 1 4
// 0 0
// 0 1
// 0 2
// 0 3
// 0 4
// 1 0
// 1 1
// 1 2
// 1 3
// 1 4
函数
函数包括以下几个部分:关键词 func,函数名,参数列表,返回列表和函数体。
直接上代码:
package main
import (
"fmt"
)
func main() {
funcA() // i am funcA
// 函数签名
fmt.Printf("%T\n", add) // func(int, int) int
fmt.Printf("%T\n", sub) // func(int, int) int
// 不定参数
fmt.Println(funcSum(1, 2)) // 3
fmt.Println(funcSum(1, 2, 3)) // 6
// slice 参数
s := []int{1, 2, 3, 4}
fmt.Println(funcSum(s...)) // 10
fmt.Println(funcSum1(s)) // 10
// 多返回值
fmt.Println(swap(1, 2)) // 2 1
x, _ := swap(1, 2)
fmt.Println(x) // 2
// 匿名函数
sum := func(a, b int) int { return a + b }
fmt.Println(sum(1, 2)) // 3
// 作为参数
fmt.Println(funcSum2(sum, 3, 5)) // 8
// 作为返回值
f := wrap("add")
fmt.Println(f(2, 4)) // 6
// 直接调用
fmt.Println(func(a, b int) int { return a + b }(4, 5)) // 9
}
func funcA() {
fmt.Println("i am funcA")
}
func add(x int, y int) int {
return x + y
}
func sub(x int, y int) (z int) {
z = x - y
return
}
// 简写形式
func add1(x, y int) int {
return x + y
}
func sub1(x, y int) (z int) {
z = x - y
return
}
// 不定参数
func funcSum(args ...int) (ret int) {
for _, arg := range args {
ret += arg
}
return
}
// slice 参数
func funcSum1(args []int) (ret int) {
for _, arg := range args {
ret += arg
}
return
}
// 多返回值
func swap(x, y int) (int, int) {
return y, x
}
// 匿名函数作为参数
func funcSum2(f func(int, int) int, x, y int) int {
return f(x, y)
}
// 匿名函数作为返回值
func wrap(op string) func(int, int) int {
switch op {
case "add":
return func(a, b int) int {
return a + b
}
case "sub":
return func(a, b int) int {
return a + b
}
default:
return nil
}
}