hello GO
这里使用vscode
- 安装插件go
-
新建hello文件夹,创建main.go文件
<code class="">package main
import "fmt"
func main(){
fmt.Println("Hello")
}
</code>- 打开命令行
-
执行go build 这时同目录会创建一个hello.exe的文件(我这里使用的是win电脑)
也可以使用 go build -o aaa.exe 指定文件名
也直接输出可使用 go run main.go
跨平台编译
只需要指定目标操作系统的平台和处理器架构即可:
<code class="language-bash ">SET CGO_ENABLED=0 // 禁用CGO
SET GOOS=linux // 目标平台是linux
SET GOARCH=amd64 // 目标处理器架构是amd64
</code>
然后再执行go build
命令,得到的就是能够在Linux平台运行的可执行文件了。
Mac 下编译 Linux 和 Windows平台 64位 可执行程序:
<code class="language-bash ">CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
</code>
Linux 下编译 Mac 和 Windows 平台64位可执行程序:
<code class="language-bash ">CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
</code>
Windows下编译Mac平台64位可执行程序:
<code class="language-bash ">SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build
</code>
基础
//单行注释
/*
多行注释
*/
fmt.Println('打印输出')
一行代码不需加分号
变量声明后必须使用,不使用会报错
缩进没有严格要求
变量和常量
标书与关键字
Go语言中标识符由字母数字和_
(下划线)组成,并且只能以字母和_
开头。
关键词
25个关键词
<code class="language-go "> 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
</code>
37个保留词
<code class="language-go "> 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
</code>
变量
声明: var 变量名 变量类型
<code class="">单个声明
var aaa string
var age int
var iss bool
批量声明
var (
a string
b int
c bool
)
</code>
变量初始化: var 变量名 类型 = 表达式
<code class="">var name string = "aa"
一次初始化多个变量
var name,age = "bb",20
</code>
短变量声明(声明并初始化) :=
<code class="">n:=10
m:=50
</code>
匿名变量:用一个下划线_
表示
匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。 (在Lua
等编程语言里,匿名变量也被叫做哑元变量。)
<code class="">func foo() (int, string) {
return 10, "Q1mi"
}
func main() {
x, _ := foo()
_, y := foo()
fmt.Println("x=", x)
fmt.Println("y=", y)
}
</code>
注意:
函数外的每个语句都必须以关键字开始(var、const、func等)
:=
不能使用在函数外。
_
多用于占位,表示忽略值。
常量const
const a = 'dsadasd'
和变量类似
<code class="">const (
a string
b int
c bool
)
</code>
iota : go语言的常量计数器
iota
在const关键字出现时将被重置为0
<code class="">const (
n1 = iota //0
n2 //1
n3 //2
n4 //3
)
使用_调过某些值
const (
n1 = iota //0
n2 //1
_
n4 //3
)
</code>
iota
声明中间插队
<code class="language-go ">const (
n1 = iota //0
n2 = 100 //100
n3 = iota //2
n4 //3
)
const n5 = iota //0
</code>
多个iota
定义在一行
<code class="language-go ">const (
a, b = iota + 1, iota + 2 //1,2
c, d //2,3
e, f //3,4
)
</code>
数据类型
整型
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 | 无符号整型,用于存放一个指针 |
进制
<code class=""> // 十进制
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
</code>
浮点型
Go语言支持两种浮点型数:float32
和float64
<code class="">package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%f\n", math.Pi)
fmt.Printf("%.2f\n", math.Pi)
}
</code>
复数
complex64和complex128
复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。
<code class="">var c1 complex64
c1 = 1 + 2i
var c2 complex128
c2 = 2 + 3i
fmt.Println(c1)
fmt.Println(c2)
</code>
布尔值
Go语言中以bool
类型进行声明布尔型数据,布尔型数据只有true(真)
和false(假)
两个值。
注意:
- 布尔类型变量的默认值为
false
。 - Go 语言中不允许将整型强制转换为布尔型.
- 布尔型无法参与数值运算,也无法与其他类型进行转换。
字符串
<code class="">s1 := "hello"
s2 := "你好"
</code>
字符串转义符
转义符 | 含义 |
---|---|
\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
。无论哪种转换,都会重新分配内存,并复制字节数组。
<code class="language-go ">func changeString() {
s1 := "big"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = 'p'
fmt.Println(string(byteS1))
s2 := "白萝卜"
runeS2 := []rune(s2)
runeS2[0] = '红'
fmt.Println(string(runeS2))
}
</code>
类型转换
强制转换
<code class="language-bash ">T(表达式)
T表示要转换的类型。表达式包括变量、复杂算子和函数返回值等.
</code>
运算符
- 算术运算符
+ - * / %
-
关系运算符
== != > >= < <=
-
逻辑运算符
&& || !
-
位运算符
运算符 | 描述 |
---|---|
& | 参与运算的两数各对应的二进位相与。 (两位均为1才为1) |
| | 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1) |
^ | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1) |
<< | 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。 |
>> | 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。 |
- 赋值运算符
= += -= *= /= %= <<= >>= &= |= ^=
流程控制
ifelse
<code class="">if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}
</code>
for
<code class="language-bash ">for 初始语句;条件表达式;结束语句{
循环体语句
}
</code>
<code class="">完整
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 {
循环体语句
}
</code>
for range(键值循环)
switch case
<code class="language-go ">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)
}
}
</code>
fallthrough
语法可以执行满足条件的case的下一个case,是为了兼容C语言中的case设计的。
<code class="language-go ">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
</code>
goto
通过标签进行代码间的无条件跳转
<code class="">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循环")
}
</code>
break(跳出循环)
break
语句可以结束for
、switch
和select
的代码块。
<code class="">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("...")
}
</code>
continue(继续下次循环)
<code class="">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)
}
}
}
</code>
数组
数组定义
var 数组变量名 [元素数量]T
一旦定义,长度不能变
初始化
<code class="">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) //[北京 上海 深圳]
}
</code>
<code class="">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
}
</code>
<code class="">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
}
</code>
数组遍历
<code class="">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)
}
}
</code>
多维数组
<code class="">func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s\t", v2)
}
fmt.Println()
}
}
北京 上海
广州 深圳
成都 重庆
</code>
多维数组只有第一层可以使用...
来让编译器推导数组长度。例如:
<code class="">//支持的写法
a := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
//不支持多维数组的内层使用...
b := [3][...]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
</code>
数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
<code class="">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]]
}
</code>
注意:
- 数组支持 “==“、”!=” 操作符,因为内存总是被初始化过的。
[n]*T
表示指针数组,*[n]T
表示数组指针 。
切片
一个拥有相同类型元素的可变长度的序列
定义
var name []T
长度/容量
通过使用内置的len()
函数求长度,使用内置的cap()
函数求切片的容量
<code class="language-go ">len(s), cap(s)
</code>
使用make()函数创建构造切片
<code class="language-bash ">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
</code>
判断切片是否为空
请始终使用len(s) == 0
来判断,而不应该使用s == nil
来判断。
切片不能直接比较
切片赋值拷贝
<code class="language-go ">s1 := make([]int, 3) //[0 0 0]
s2 := s1 //将s1直接赋值给s2,s1和s2共用一个底层数组
</code>
切片遍历
遍历方式和数组是一致的
append
<code class="language-go ">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]
}
</code>
var声明的零值切片可以在append()
函数直接使用,无需初始化。
<code class="">s := []int{} // 没有必要初始化
s = append(s, 1, 2, 3)
var s = make([]int) // 没有必要初始化
s = append(s, 1, 2, 3)
</code>
追加切片
<code class="">var citySlice []string
// 追加一个元素
citySlice = append(citySlice, "北京")
// 追加多个元素
citySlice = append(citySlice, "上海", "广州", "深圳")
// 追加切片
a := []string{"成都", "重庆"}
citySlice = append(citySlice, a...)
fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]
</code>
copy
<code class="language-bash ">copy(destSlice, srcSlice []T)
srcSlice: 数据来源切片
destSlice: 目标切片
</code>
<code class="language-go ">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]
}
</code>
删除元素
没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素
从切片a中删除索引为index
的元素,操作方法是a = append(a[:index], a[index+1:]...)
<code class="language-go ">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]
}
</code>
map
map定义
<code class="language-go ">map[KeyType]ValueType
KeyType:表示键的类型。
ValueType:表示键对应的值的类型。
</code>
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
<code class="language-go ">make(map[KeyType]ValueType, [cap])
</code>
基本使用
<code class="language-go ">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) //
}
</code>
判断某个键是否存在
<code class="language-go ">value, ok := map[key]
</code>
<code class="language-go ">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("查无此人")
}
}
</code>
map遍历
<code class="language-go ">func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
</code>
遍历map时的元素顺序与添加键值对的顺序无关。
delete
<code class="language-go ">delete(map, key)
</code>
<code class="language-go ">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)
}
}
</code>
按照指定顺序遍历map
<code class="language-go ">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])
}
}
</code>
元素为map类型的切片
<code class="">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)
}
}
</code>
值为切片类型的map
<code class="language-go ">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)
</code>