hello GO
这里使用vscode
- 安装插件go
-
新建hello文件夹,创建main.go文件
123456package mainimport "fmt"func main(){fmt.Println("Hello")}- 打开命令行
- 打开命令行
-
执行go build 这时同目录会创建一个hello.exe的文件(我这里使用的是win电脑)
也可以使用 go build -o aaa.exe 指定文件名
也直接输出可使用 go run main.go
跨平台编译
只需要指定目标操作系统的平台和处理器架构即可:
1 2 3 4 |
SET CGO_ENABLED=0 // 禁用CGO SET GOOS=linux // 目标平台是linux SET GOARCH=amd64 // 目标处理器架构是amd64 |
然后再执行go build
命令,得到的就是能够在Linux平台运行的可执行文件了。
Mac 下编译 Linux 和 Windows平台 64位 可执行程序:
1 2 3 |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build |
Linux 下编译 Mac 和 Windows 平台64位可执行程序:
1 2 3 |
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build |
Windows下编译Mac平台64位可执行程序:
1 2 3 4 5 |
SET CGO_ENABLED=0 SET GOOS=darwin SET GOARCH=amd64 go build |
基础
//单行注释
/*
多行注释
*/
fmt.Println(‘打印输出’)
一行代码不需加分号
变量声明后必须使用,不使用会报错
缩进没有严格要求
变量和常量
标书与关键字
Go语言中标识符由字母数字和_
(下划线)组成,并且只能以字母和_
开头。
关键词
25个关键词
1 2 3 4 5 6 |
break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var |
37个保留词
1 2 3 4 5 6 7 8 9 10 11 |
Constants: true false iota nil Types: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex128 complex64 bool byte rune string error Functions: make len cap new append copy close delete complex real imag panic recover |
变量
声明: var 变量名 变量类型
1 2 3 4 5 6 7 8 9 10 11 12 |
单个声明 var aaa string var age int var iss bool 批量声明 var ( a string b int c bool ) |
变量初始化: var 变量名 类型 = 表达式
1 2 3 4 5 |
var name string = "aa" 一次初始化多个变量 var name,age = "bb",20 |
短变量声明(声明并初始化) :=
1 2 3 |
n:=10 m:=50 |
匿名变量:用一个下划线_
表示
匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。 (在Lua
等编程语言里,匿名变量也被叫做哑元变量。)
1 2 3 4 5 6 7 8 9 10 |
func foo() (int, string) { return 10, "Q1mi" } func main() { x, _ := foo() _, y := foo() fmt.Println("x=", x) fmt.Println("y=", y) } |
注意:
函数外的每个语句都必须以关键字开始(var、const、func等)
:=
不能使用在函数外。
_
多用于占位,表示忽略值。
常量const
const a = 'dsadasd'
和变量类似
1 2 3 4 5 6 |
const ( a string b int c bool ) |
iota : go语言的常量计数器
iota
在const关键字出现时将被重置为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const ( n1 = iota //0 n2 //1 n3 //2 n4 //3 ) 使用_调过某些值 const ( n1 = iota //0 n2 //1 _ n4 //3 ) |
iota
声明中间插队
1 2 3 4 5 6 7 8 |
const ( n1 = iota //0 n2 = 100 //100 n3 = iota //2 n4 //3 ) const n5 = iota //0 |
多个iota
定义在一行
1 2 3 4 5 6 |
const ( a, b = iota + 1, iota + 2 //1,2 c, d //2,3 e, f //3,4 ) |
数据类型
整型
Go语言中有丰富的数据类型,除了基本的整型、浮点型、布尔型、字符串外,还有数组、切片、结构体、函数、map、通道(channel)等。
类型 | 描述 |
---|---|
uint8 | 无符号 8位整型 (0 到 255) |
uint16 | 无符号 16位整型 (0 到 65535) |
uint32 | 无符号 32位整型 (0 到 4294967295) |
uint64 | 无符号 64位整型 (0 到 18446744073709551615) |
int8 | 有符号 8位整型 (-128 到 127) |
int16 | 有符号 16位整型 (-32768 到 32767) |
int32 | 有符号 32位整型 (-2147483648 到 2147483647) |
int64 | 有符号 64位整型 (-9223372036854775808 到 9223372036854775807) |
特殊整型
类型 | 描述 |
---|---|
uint | 32位操作系统上就是uint32 ,64位操作系统上就是uint64 |
int | 32位操作系统上就是int32 ,64位操作系统上就是int64 |
uintptr | 无符号整型,用于存放一个指针 |
进制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 十进制 var a int = 10 fmt.Printf("%d \n", a) // 10 fmt.Printf("%b \n", a) // 1010 占位符%b表示二进制 // 八进制 以0开头 var b int = 077 fmt.Printf("%o \n", b) // 77 // 十六进制 以0x开头 var c int = 0xff fmt.Printf("%x \n", c) // ff fmt.Printf("%X \n", c) // FF |
浮点型
Go语言支持两种浮点型数:float32
和float64
1 2 3 4 5 6 7 8 9 10 |
package main import ( "fmt" "math" ) func main() { fmt.Printf("%f\n", math.Pi) fmt.Printf("%.2f\n", math.Pi) } |
复数
complex64和complex128
复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。
1 2 3 4 5 6 7 |
var c1 complex64 c1 = 1 + 2i var c2 complex128 c2 = 2 + 3i fmt.Println(c1) fmt.Println(c2) |
布尔值
Go语言中以bool
类型进行声明布尔型数据,布尔型数据只有true(真)
和false(假)
两个值。
注意:
- 布尔类型变量的默认值为
false
。 - Go 语言中不允许将整型强制转换为布尔型.
- 布尔型无法参与数值运算,也无法与其他类型进行转换。
字符串
1 2 3 |
s1 := "hello" s2 := "你好" |
字符串转义符
转义符 | 含义 |
---|---|
\r |
回车符(返回行首) |
\n |
换行符(直接跳到下一行的同列位置) |
\t |
制表符 |
\' |
单引号 |
\" |
双引号 |
\\ |
反斜杠 |
多行字符串 使用反引号
字符串的常用操作
方法 | 介绍 |
---|---|
len(str) | 求长度 |
+或fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置 |
strings.Join(a[]string, sep string) | join操作 |
byte和rune类型
字符用单引号(’)包裹起来
nt8
类型,或者叫 byte 型,代表了ASCII码
的一个字符。rune
类型,代表一个UTF-8字符
。
当需要处理中文、日文或者其他复合字符时,则需要用到rune
类型。rune
类型实际是一个int32
修改字符串
要修改字符串,需要先将其转换成[]rune
或[]byte
,完成后再转换为string
。无论哪种转换,都会重新分配内存,并复制字节数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func changeString() { s1 := "big" // 强制类型转换 byteS1 := []byte(s1) byteS1[0] = 'p' fmt.Println(string(byteS1)) s2 := "白萝卜" runeS2 := []rune(s2) runeS2[0] = '红' fmt.Println(string(runeS2)) } |
类型转换
强制转换
1 2 3 |
T(表达式) T表示要转换的类型。表达式包括变量、复杂算子和函数返回值等. |
运算符
- 算术运算符
+ - * / %
-
关系运算符
== != > >= < <=
-
逻辑运算符
&& || !
-
位运算符
运算符 | 描述 |
---|---|
& | 参与运算的两数各对应的二进位相与。 (两位均为1才为1) |
| | 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1) |
^ | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1) |
<< | 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。 |
>> | 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。 |
- 赋值运算符
= += -= *= /= %= <<= >>= &= |= ^=
流程控制
ifelse
1 2 3 4 5 6 7 8 |
if 表达式1 { 分支1 } else if 表达式2 { 分支2 } else{ 分支3 } |
for
1 2 3 4 |
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 |
完整 func forDemo() { for i := 0; i < 10; i++ { fmt.Println(i) } } 初始语句省略 func forDemo2() { i := 0 for ; i < 10; i++ { fmt.Println(i) } } 初始语句/结束语句结束 func forDemo3() { i := 0 for i < 10 { fmt.Println(i) i++ } } 无限循环 for { 循环体语句 } |
for range(键值循环)
switch case
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 |
func switchDemo1() { finger := 3 switch finger { case 1: fmt.Println("大拇指") case 2: fmt.Println("食指") case 3: fmt.Println("中指") case 4: fmt.Println("无名指") case 5: fmt.Println("小拇指") default: fmt.Println("无效的输入!") } } func testSwitch3() { switch n := 7; n { case 1, 3, 5, 7, 9: fmt.Println("奇数") case 2, 4, 6, 8: fmt.Println("偶数") default: fmt.Println(n) } } |
fallthrough
语法可以执行满足条件的case的下一个case,是为了兼容C语言中的case设计的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func switchDemo5() { s := "a" switch { case s == "a": fmt.Println("a") fallthrough case s == "b": fmt.Println("b") case s == "c": fmt.Println("c") default: fmt.Println("...") } } 输出: a b |
goto
通过标签进行代码间的无条件跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func gotoDemo2() { for i := 0; i < 10; i++ { for j := 0; j < 10; j++ { if j == 2 { // 设置退出标签 goto breakTag } fmt.Printf("%v-%v\n", i, j) } } return // 标签 breakTag: fmt.Println("结束for循环") } |
break(跳出循环)
break
语句可以结束for
、switch
和select
的代码块。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func breakDemo1() { BREAKDEMO1: for i := 0; i < 10; i++ { for j := 0; j < 10; j++ { if j == 2 { break BREAKDEMO1 } fmt.Printf("%v-%v\n", i, j) } } fmt.Println("...") } |
continue(继续下次循环)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func continueDemo() { forloop1: for i := 0; i < 5; i++ { // forloop2: for j := 0; j < 5; j++ { if i == 2 && j == 2 { continue forloop1 } fmt.Printf("%v-%v\n", i, j) } } } |
数组
数组定义
var 数组变量名 [元素数量]T
一旦定义,长度不能变
初始化
1 2 3 4 5 6 7 8 9 |
func main() { var testArray [3]int //数组会初始化为int类型的零值 var numArray = [3]int{1, 2} //使用指定的初始值完成初始化 var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化 fmt.Println(testArray) //[0 0 0] fmt.Println(numArray) //[1 2 0] fmt.Println(cityArray) //[北京 上海 深圳] } |
1 2 3 4 5 6 7 8 9 10 11 |
func main() { var testArray [3]int var numArray = [...]int{1, 2} var cityArray = [...]string{"北京", "上海", "深圳"} fmt.Println(testArray) //[0 0 0] fmt.Println(numArray) //[1 2] fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int fmt.Println(cityArray) //[北京 上海 深圳] fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string } |
1 2 3 4 5 6 |
func main() { a := [...]int{1: 1, 3: 5} fmt.Println(a) // [0 1 0 5] fmt.Printf("type of a:%T\n", a) //type of a:[4]int } |
数组遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func main() { var a = [...]string{"北京", "上海", "深圳"} // 方法1:for循环遍历 for i := 0; i < len(a); i++ { fmt.Println(a[i]) } // 方法2:for range遍历 for index, value := range a { fmt.Println(index, value) } } |
多维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func main() { a := [3][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } for _, v1 := range a { for _, v2 := range v1 { fmt.Printf("%s\t", v2) } fmt.Println() } } 北京 上海 广州 深圳 成都 重庆 |
多维数组只有第一层可以使用...
来让编译器推导数组长度。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//支持的写法 a := [...][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } //不支持多维数组的内层使用... b := [3][...]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } |
数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func modifyArray(x [3]int) { x[0] = 100 } func modifyArray2(x [3][2]int) { x[2][0] = 100 } func main() { a := [3]int{10, 20, 30} modifyArray(a) //在modify中修改的是a的副本x fmt.Println(a) //[10 20 30] b := [3][2]int{ {1, 1}, {1, 1}, {1, 1}, } modifyArray2(b) //在modify中修改的是b的副本x fmt.Println(b) //[[1 1] [1 1] [1 1]] } |
注意:
- 数组支持 “==“、”!=” 操作符,因为内存总是被初始化过的。
[n]*T
表示指针数组,*[n]T
表示数组指针 。
切片
一个拥有相同类型元素的可变长度的序列
定义
var name []T
长度/容量
通过使用内置的len()
函数求长度,使用内置的cap()
函数求切片的容量
1 2 |
len(s), cap(s) |
使用make()函数创建构造切片
1 2 3 4 5 6 7 8 9 10 11 |
make([]T, size, cap) T:切片的元素类型 size:切片中元素的数量 cap:切片的容量 a := make([]int, 2, 10) fmt.Println(a) //[0 0] fmt.Println(len(a)) //2 fmt.Println(cap(a)) //10 |
判断切片是否为空
请始终使用len(s) == 0
来判断,而不应该使用s == nil
来判断。
切片不能直接比较
切片赋值拷贝
1 2 3 |
s1 := make([]int, 3) //[0 0 0] s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组 |
切片遍历
遍历方式和数组是一致的
append
1 2 3 4 5 6 7 8 |
func main(){ var s []int s = append(s, 1) // [1] s = append(s, 2, 3, 4) // [1 2 3 4] s2 := []int{5, 6, 7} s = append(s, s2...) // [1 2 3 4 5 6 7] } |
var声明的零值切片可以在append()
函数直接使用,无需初始化。
1 2 3 4 5 6 |
s := []int{} // 没有必要初始化 s = append(s, 1, 2, 3) var s = make([]int) // 没有必要初始化 s = append(s, 1, 2, 3) |
追加切片
1 2 3 4 5 6 7 8 9 10 |
var citySlice []string // 追加一个元素 citySlice = append(citySlice, "北京") // 追加多个元素 citySlice = append(citySlice, "上海", "广州", "深圳") // 追加切片 a := []string{"成都", "重庆"} citySlice = append(citySlice, a...) fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆] |
copy
1 2 3 4 |
copy(destSlice, srcSlice []T) srcSlice: 数据来源切片 destSlice: 目标切片 |
1 2 3 4 5 6 7 8 9 10 11 12 |
func main() { // copy()复制切片 a := []int{1, 2, 3, 4, 5} c := make([]int, 5, 5) copy(c, a) //使用copy()函数将切片a中的元素复制到切片c fmt.Println(a) //[1 2 3 4 5] fmt.Println(c) //[1 2 3 4 5] c[0] = 1000 fmt.Println(a) //[1 2 3 4 5] fmt.Println(c) //[1000 2 3 4 5] } |
删除元素
没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素
从切片a中删除索引为index
的元素,操作方法是a = append(a[:index], a[index+1:]...)
1 2 3 4 5 6 7 8 |
func main() { // 从切片中删除元素 a := []int{30, 31, 32, 33, 34, 35, 36, 37} // 要删除索引为2的元素 a = append(a[:2], a[3:]...) fmt.Println(a) //[30 31 33 34 35 36 37] } |
map
map定义
1 2 3 4 |
map[KeyType]ValueType KeyType:表示键的类型。 ValueType:表示键对应的值的类型。 |
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
1 2 |
make(map[KeyType]ValueType, [cap]) |
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
func main() { scoreMap := make(map[string]int, 8) scoreMap["张三"] = 90 scoreMap["小明"] = 100 fmt.Println(scoreMap) fmt.Println(scoreMap["小明"]) fmt.Printf("type of a:%T\n", scoreMap) } map[小明:100 张三:90] 100 type of a:map[string]int map也支持在声明的时候填充元素 func main() { userInfo := map[string]string{ "username": "沙河小王子", "password": "123456", } fmt.Println(userInfo) // } |
判断某个键是否存在
1 2 |
value, ok := map[key] |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func main() { scoreMap := make(map[string]int) scoreMap["张三"] = 90 scoreMap["小明"] = 100 // 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值 v, ok := scoreMap["张三"] if ok { fmt.Println(v) } else { fmt.Println("查无此人") } } |
map遍历
1 2 3 4 5 6 7 8 9 10 |
func main() { scoreMap := make(map[string]int) scoreMap["张三"] = 90 scoreMap["小明"] = 100 scoreMap["娜扎"] = 60 for k, v := range scoreMap { fmt.Println(k, v) } } |
遍历map时的元素顺序与添加键值对的顺序无关。
delete
1 2 |
delete(map, key) |
1 2 3 4 5 6 7 8 9 10 11 |
func main(){ scoreMap := make(map[string]int) scoreMap["张三"] = 90 scoreMap["小明"] = 100 scoreMap["娜扎"] = 60 delete(scoreMap, "小明")//将小明:100从map中删除 for k,v := range scoreMap{ fmt.Println(k, v) } } |
按照指定顺序遍历map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
func main() { rand.Seed(time.Now().UnixNano()) //初始化随机数种子 var scoreMap = make(map[string]int, 200) for i := 0; i < 100; i++ { key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串 value := rand.Intn(100) //生成0~99的随机整数 scoreMap[key] = value } //取出map中的所有key存入切片keys var keys = make([]string, 0, 200) for key := range scoreMap { keys = append(keys, key) } //对切片进行排序 sort.Strings(keys) //按照排序后的key遍历map for _, key := range keys { fmt.Println(key, scoreMap[key]) } } |
元素为map类型的切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func main() { var mapSlice = make([]map[string]string, 3) for index, value := range mapSlice { fmt.Printf("index:%d value:%v\n", index, value) } fmt.Println("after init") // 对切片中的map元素进行初始化 mapSlice[0] = make(map[string]string, 10) mapSlice[0]["name"] = "小王子" mapSlice[0]["password"] = "123456" mapSlice[0]["address"] = "沙河" for index, value := range mapSlice { fmt.Printf("index:%d value:%v\n", index, value) } } |
值为切片类型的map
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func main() { var sliceMap = make(map[string][]string, 3) fmt.Println(sliceMap) fmt.Println("after init") key := "中国" value, ok := sliceMap[key] if !ok { value = make([]string, 0, 2) } value = append(value, "北京", "上海") sliceMap[key] = value fmt.Println(sliceMap) |