GO_基础1
一、环境准备
参考:https://www.cainiaojc.com/golang/go-environment-install.html
-
Vscode环境配置
-
插件安装
- go插件:https://marketplace.visualstudio.com/items?itemName=golang.Go
- Code Debugger插件:https://marketplace.visualstudio.com/items?itemName=wowbox.code-debuger
-
环境变量设置:
- GOROOT:
-
GoROOT环境变量通常指向Go的安装位置。这个路径包含了Go的编译器(cmd/gc或后续的cmd/go)、标准库的源码以及其他与Go运行时系统相关的组件。
-
-
- GOPATH
-
与GoROOT不同,GoPATH是一个用于存放Go项目依赖库、第三方包以及你的Go代码的目录。它是一个工作空间,让你可以管理多个Go项目而不会互相冲突。
-
-
配置lanuch.json配置文件。
-
二、GO 文件结构
-
基本结构
-
包声明
-
引入包
-
变量
-
函数
-
语句表达式
-
注释
-
// 包声明 package main //导入fmt包 import "fmt" // 函数声明 func printInConsole(s string) { fmt.Println(s) } // 全局变量声明 var str string = "Hello, world" // 主函数 func main() { printInConsole(str) //调用函数 }
-
-
初始化顺序
初始化底层包的顺序: 常量 → 变量 → init函数, 其中init函数有go的runtime调用。

三、基本数据类型
-
基本类型
-
数字
-
整形
int8uint8int16uint16int32uint32int64uint64intuint,具体长度取决于 CPU 位数。
-
浮点性
float32float64- var float1 float32 = 10
- float2 := 10.0 (自动类型推导)自动设置为float64.
-
复数
- complex64
- complex128
-
-
var a complex64 = a + bi a是实部, b是虚部,当a = 0是 a就是常见的实数, 当b= 0是, a就是纯虚数。
-
-
-
- go 提供内置函数real和imag, 分别回去复数的实部和虚部
-
-
c1 = 1.10 + 0.1i
x := real(c1)
y := imag(c1)
-
-
-
byte类型
- byte是uint8的别名, 可以吧byte和uint8视为同一种类型。
-
package main import "fmt" func main() { var s string = "Hello, world!" var bytes []byte = []byte(s) fmt.Println(bytes) fmt.Println(string(bytes)) fmt.Println(string(bytes) == s) }
结果:[72 101 108 108 111 44 32 119 111 114 108 100 33]Hello, world!true
-
-
-
-
-
rune类型
-
-
rune是uint32的别名, 可以吧rune和uint32视为同一种类型。
-
-
字符串
-
string
只能用一对双引号("")或反引号(``)括起来定义,不能用单引号('')定义!
-
-
布尔
-
bool
只有 true 和 false,默认为 false。
-
-
零值
-
数字类型是0
字符串类型是空
布尔类型是false
接口和引用类型为nil
-
聚合类型
-
数组
-
声明
-
func main() { // 仅声明 var a [5]int fmt.Println("a = ", a) // 声明以及初始化 var b [5]int = [5]int{1, 2, 3, 4, 5} fmt.Println("b = ", b) // 类型推导声明方式 var c = [5]string{"c1", "c2", "c3", "c4", "c5"} fmt.Println("c = ", c) d := [3]int{3, 2, 1} fmt.Println("d = ", d) // 使用 ... 代替数组长度 autoLen := [...]string{"auto1", "auto2", "auto3"} fmt.Println("autoLen = ", autoLen) // 声明时初始化指定下标的元素值 positionInit := [5]string{1: "position1", 3: "position3"} fmt.Println("positionInit = ", positionInit) // 初始化时,元素个数不能超过数组声明的长度 //overLen := [2]int{1, 2, 3} }
-
-
访问
-
func main() { a := [5]int{5, 4, 3, 2, 1} // 方式1,使用下标读取数据 element := a[2] fmt.Println("element = ", element) // 方式2,使用range遍历 for i, v := range a { fmt.Println("index = ", i, "value = ", v) } for i := range a { fmt.Println("only index, index = ", i) } // 读取数组长度 fmt.Println("len(a) = ", len(a)) // 使用下标,for循环遍历数组 for i := 0; i < len(a); i++ { fmt.Println("use len(), index = ", i, "value = ", a[i]) } }
-
-
多维数组
-
func main() { // 二维数组 a := [3][2]int{ {0, 1}, {2, 3}, {4, 5}, } fmt.Println("a = ", a) // 三维数组 b := [3][2][2]int{ {{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}, {{8, 9}, {10, 11}}, } fmt.Println("b = ", b) // 也可以省略各个位置的初始化,在后续代码中赋值 c := [3][3][3]int{} c[2][2][1] = 5 c[1][2][1] = 4 fmt.Println("c = ", c) }
-
数组作为参数
-
-
-
切片
-
-
-
-
声明初始化
-
// 方式1,声明并初始化一个空的切片 var s1 []int = []int{} // 方式2,类型推导,并初始化一个空的切片 var s2 = []int{} // 方式3,与方式2等价 s3 := []int{} // 方式4,与方式1、2、3 等价,可以在大括号中定义切片初始元素 s4 := []int{1, 2, 3, 4} // 方式5,用make()函数创建切片,创建[]int类型的切片,指定切片初始长度为0 s5 := make([]int, 0) // 方式6,用make()函数创建切片,创建[]int类型的切片,指定切片初始长度为2,指定容量参数4 s6 := make([]int, 2, 4) // 方式7,引用一个数组,初始化切片 a := [5]int{6,5,4,3,2} // 从数组下标2开始,直到数组的最后一个元素 s7 := arr[2:] // 从数组下标1开始,直到数组下标3的元素,创建一个新的切片 s8 := arr[1:3] // 从0到下标2的元素,创建一个新的切片 s9 := arr[:2]
-
-
使用切片
-
-
-
-
-
访问
- 切片访问和数组一样
- 切片还可以使用
len()和cap()函数访问切片的长度和容量。当切片是 nil 时,len()和cap()函数获取的到值都是 0。
-
添加
-
ppend函数追加元素 s3 = append(s3) s3 = append(s3, 1)
-
-
复制
- 可以使用内置函数
copy()把某个切片中的所有元素复制到另一个切片,复制的长度是它们中最短的切片长度。
- 可以使用内置函数
-
-
map集合
- 声明
-
方式 1,仅声明 map: var <map name> map[<key type>]<value type> 方式 2,使用内置函数 make() 初始化: <map name> := make(map[<key type>]<value type>) // 还可以使用map,提前指定容量 <map name> := make(map[<key type>]<value type>, <capacity>) 指定合适的初始容量,可以减少使用 map 存储键值对时触发扩容,减少扩容操作可以提高一些使用 map 的性能。 方式 3,在初始化时,同时插入键值对: // 不会插入任何键值对 <map name> := map[<key type>]<value type> {} // 插入键值对 <map name> := map[<key type>]<value type> { <key1>: <value1>, <key2>: <value2>, ... }
-
-
使用
-
获取元素: <value> := <map name>[<key>] <value>,<exist flag> := <map name>[<key>] 插入或修改键值对: <map name>[<key>] = <value> 通过内置函数 len() 获取 map 中键值对数量: length := len(<map name>) 遍历 map 集合: for <key name>, <value name> := range <map name> { <expression> ... } for <key name> := range <map name> { <expression> ... } 使用内置函数 delete() 删除 map 集合中指定 key: delete(<map name>, <key>)
-
- 声明
-
结构体
-
- go中没有类的概念
- go中全局变量、全局常量、结构体、字段、方法只有公开类型和非公开类型。公开类型首字母大写, 非公开类型首字母小写。
-
-
-
定义结构体
type Address struct { name string street string city string state string Pincode int }
-
定义匿名字段
-
-
结构体中的字段不是一定要有字段名,也可以仅定义类型,这种只有类型没有字段名的字段被称为匿名字段
-
-
-
-
type Custom struct { int string Other string }
-
-
匿名结构体
-
-
匿名结构体是没有定义名称的结构体
- 方式 1,仅可在函数外声明,这种方式可以看成是声明了一个匿名的结构体,实例化后赋值给了的全局变量:
-
var <var name> = struct { <FiledName1> <type1> `<tag1>:"<any string>"` <FiledName2> <type2> `<tag2>:"<any string>"` ... <type3> <type4> ... } { <FiledName1>: <value1>, <FiledName2>: <value2>, ... <type3>: <value3>, <type4>: <value4>, }
-
-
-
枚举
-
go中没有内置枚举类型,所以go中的枚举是使用const来定义枚举的。
枚举的本质就是一系列的常量。所以 Go 中使用 const 定义枚举,比如:
-
-
-
const ( Male = "Male" Female = "Female" )
-
-
除了上面的别名类型来声明枚举类型以外,还可以使用 iota 关键字,来自动为常量赋值。iota关键字 -
range关键字
-
range 关键字用于 for 循环迭代字符串(string)、数组(array)、切片(slice)、通道(channel)或映射集合(map)中的元素
-
-
-
对字符串迭代
-
str1 := "abc123" for index := range str1 { fmt.Printf("str1 -- index:%d, value:%d\n", index, str1[index]) }
-
-
对数组与切片迭代
-
// 方法1:只拿到数组的下标索引 for index := range array { fmt.Printf("array -- index=%d value=%d \n", index, array[index]) } for index := range slice { fmt.Printf("slice -- index=%d value=%d \n", index, slice[index]) } fmt.Println() // 方法2:同时拿到数组的下标索引和对应的值 for index, value := range array { fmt.Printf("array -- index=%d index value=%d \n", index, array[index]) fmt.Printf("array -- index=%d range value=%d \n", index, value) } for index, value := range slice { fmt.Printf("slice -- index=%d index value=%d \n", index, slice[index]) fmt.Printf("slice -- index=%d range value=%d \n", index, value) }
-
-
对通道迭代
-
for i := range ch { fmt.Println(i) }
-
-
对映射集合迭代
-
package main import "fmt" func main() { hash := map[string]int{ "a": 1, "f": 2, "z": 3, "c": 4, } for key := range hash { fmt.Printf("key=%s, value=%d\n", key, hash[key]) } for key, value := range hash { fmt.Printf("key=%s, value=%d\n", key, value) } }
-
-
-
-
引用类型
-
指针
-
声明和初始化
-
package main import "fmt" func main() { //定义一个正常变量 var x int = 5748 //指针声明 var p *int //初始化指针 p = &x //显示结果 fmt.Println("存储在x中的值 = ", x) fmt.Println("x的内存地址 = ", &x) fmt.Println("存储在变量p中的值 = ", p) }输出:存储在x中的值 = 5748 x的内存地址 = 0xc000066090 存储在变量p中的值 = 0xc000066090 -
指针解引用
-
package main import "fmt" func main() { //使用var关键字 //我们没有定义 //任何带变量的类型 var y = 458 //使用指针变量 // var关键字,不指定 //类型 var p = &y fmt.Println("存储在y中的值 = ", y) fmt.Println("y的地址= ", &y) fmt.Println("存储在指针变量p中的值 = ", p) //这是取消引用指针 //在指针之前使用*运算符 //变量以访问存储的值 //指向它所指向的变量 fmt.Println("存储在y中的值(*p) = ", *p) } 结果: 存储在y中的值 = 458 y的地址= 0xc0000120a8 存储在指针变量p中的值 = 0xc0000120a8 存储在y中的值(*p) = 458
-
双指针
-
-
-
类型转换
- 注意
- 不支持自动类型转换或隐式类型转换,对于类型转换,必须执行显式转换。
-
package main import "fmt" func main() { var totalsum int = 446 var number int = 23 var avg float32 // 显式类型转换 avg = float32(totalsum) / float32(number) // 显示结果 fmt.Printf("平均值 = %f\n", avg) }
-
- 不允许在表达式中混合使用数字类型(例如加,减,乘,除等),并且不允许在两个混合类型之间执行赋值类型。
-
var cainiaojc1 int64 = 875 //它会在编译时抛出错误给我们 //因为正在执行混合类型,例如把int64作为int类型 var cainiaojc2 int = cainiaojc1 var cainiaojc3 int = 100 //它抛出编译时错误 //这是无效操作 //因为类型是混合的 int64 和 int 相加 var addition = cainiaojc1 + cainiaojc3
-
-
-
数字类型转换
- 数字类型之间相互转换比较简单,并且位数较多的类型向位数较少的类型转换时,高位数据会被直接截去。
-
package main import "fmt" func main() { var i int32 = 17 var b byte = 5 var f float32 // 数字类型可以直接强转 f = float32(i) / float32(b) fmt.Printf("f 的值为: %f\n", f) // 当int32类型强转成byte时,高位被直接舍弃 var i2 int32 = 256 var b2 byte = byte(i2) fmt.Printf("b2 的值为: %d\n", b2) }
-
字符串类型转换
-
string 类型、[]byte 类型与[]rune 类型之间可以类似数字类型那样相互转换,并且数据不会有任何丢失
-
-
-
package main import "fmt" func main() { str := "hello, 123, 你好" var bytes []byte = []byte(str) var runes []rune = []rune(str) fmt.Printf("bytes 的值为: %v \n", bytes) fmt.Printf("runes 的值为: %v \n", runes) str2 := string(bytes) str3 := string(runes) fmt.Printf("str2 的值为: %v \n", str2) fmt.Printf("str3 的值为: %v \n", str3) }
数字与字符串相互转换的需求,这时需要使用到 go 提供的标准库
strconv -
package main import ( "fmt" "strconv" ) func main() { str := "123" num, err := strconv.Atoi(str) if err != nil { panic(err) } fmt.Printf("字符串转换为int: %d \n", num) str1 := strconv.Itoa(num) fmt.Printf("int转换为字符串: %s \n", str1) ui64, err := strconv.ParseUint(str, 10, 32) fmt.Printf("字符串转换为uint64: %d \n", num) str2 := strconv.FormatUint(ui64, 2) fmt.Printf("uint64转换为字符串: %s \n", str2) } 最常见的转换是字符串与 int 类型之间相互转换。也就是 Atoi 方法与 Itoa 方法。 当需要把字符串转换成无符号数字时,目前只能转换成 uint64 类型,需要其他位的数字类型需要从 uint64 类型转到所需的数字类型。 同时可以看到当使用 ParseUint 方法把字符串转换成数字时,或者使用 FormatUint 方法把数字转换成字符串时,都需要提供第二个参数 base,这个参数表示的是数字的进制,即标识字符串输出或输入的数字进制。
-
-
接口类型转换
-
package main import "fmt" func main() { var i interface{} = 3 a, ok := i.(int) if ok { fmt.Printf("'%d' is a int \n", a) } else { fmt.Println("conversion failed") } }
-
-
结构体类型转换
-
package main import "fmt" type SameFieldA struct { name string value int } type SameFieldB struct { name string value int } func (s *SameFieldB) getValue() int { return s.value } func main() { a := SameFieldA{ name: "a", value: 1, } b := SameFieldB(a) fmt.Printf("conver SameFieldA to SameFieldB, value is : %d \n", b.getValue()) // 只能结构体类型实例之间相互转换,指针不可以相互转换 // var c interface{} = &a // _, ok := c.(*SameFieldB) // fmt.Printf("c is *SameFieldB: %v \n", ok) }
-
-
四、变量、常量和运算符
-
变量
-
使用var关键字声明
-
package main import "fmt" func main() { //变量声明和初始化 //显式类型 var myvariable1 = 20 var myvariable2 = "cainiaojc" var myvariable3 = 34.80 // Display the value and the // type of the variables fmt.Printf("myvariable1的值是 : %d\n", myvariable1) fmt.Printf("myvariable1的类型是 : %T\n", myvariable1) fmt.Printf("myvariable2的值是 : %s\n", myvariable2) fmt.Printf("myvariable2的类型是 : %T\n", myvariable2) fmt.Printf("myvariable3的值是 : %f\n", myvariable3) fmt.Printf("myvariable3的类型是 : %T\n", myvariable3) }
结果myvariable1的值是 : 20
myvariable1的类型是 : int
myvariable2的值是 : cainiaojc
myvariable2的类型是 : string
myvariable3的值是 : 34.800000
myvariable3的类型是 : float64- 使用var关键字的要点
- 在使用var关键字声明变量期间,可以省略 type 或= 表达式,但不能同时省略。如果这样做,编译器将抛出一个错误
- Go语言中没有这样的未初始化变量的概念。
- 使用var关键字的要点
-
使用短变量声明
- variable_name:= expressio
-
package main import "fmt" func main() { // 使用短变量声明 myvariable1 := 39 myvariable2 := "(cainiaojc.com)" myvariable3 := 34.67 // 打印变量值和类型 fmt.Printf("myvariable1的值是 : %d\n", myvariable1) fmt.Printf("myvariable1的类型是 : %T\n", myvariable1) fmt.Printf("\nmyvariable2的值是 : %s\n", myvariable2) fmt.Printf("myvariable2的类型是 : %T\n", myvariable2) fmt.Printf("\nmyvariable3的值是 : %f\n", myvariable3) fmt.Printf("myvariable3的类型是 : %T\n", myvariable3) }
结果:myvariable1的值是 : 39 myvariable1的类型是 : int myvariable2的值是 : (cainiaojc.com) myvariable2的类型是 : string myvariable3的值是 : 34.670000 myvariable3的类型是 : float64
-
-
常量
使用const 关键字作为前缀来声明具有特定类型的常量。不能使用:=语法声明。
-
-
分类
-
数值常量(整数常量,浮点常量,复数常量)
-
字符串字面量
-
布尔常量
-
-
-
运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 按位运算符
- 赋值运算符
- 杂项运算符

浙公网安备 33010602011771号