文章目录
前言
计划是花三四天把基本的语法学完,然后开始学习6.824了,后面的编程都需要用GO了。
GO基本语法学习之路 Day1
1. 变量声明
变量声明有四种方式:
var a int // 方法一:声明一个变量 默认值是0
var b int = 100 // 方法二:声明一个变量,初始化一个值
var c = 100 // 方法三:初始化的时候,省去数据类型,通过值匹配当前的变量数据类型
e := 100 // 方法四:(常用的方法)省去var关键字,直接自动匹配 不支持全局
声明多个变量:
var xx, yy = 100, "JehanRio" // 单行
var ( // 多行
vv int = 100
jj bool = true
kk = 3.14
)
2. const与iota
常量的创建一样:const length = 10
iota类似于C/C++中的枚举类型,进行累加,只能在const中使用
const (
// 在const()添加一个关键字 iota,每行的iota都会累加一,第一行是默认0(只能出现在const的括号中)
CHONGQING = 7
CHENGDU
NANJING = 0
SHANGHAI = iota*10 // 第n行,iota的初始值就是n-1
SHENZHEN
)
3. 函数
基本方式:
func foo1(a string, b string) int {
fmt.Println("a=", a)
fmt.Println("b=", b)
c := 100
return c
}
当然,还能返回多个值,像python一样
func foo2(a string, b string) (int, int) {
return 1, 2
}
返回多个返回值,有形参名的, 默认值为0
func foo3() (r1 int, r2 int) {
// 给有名称的返回值赋值
r1 = 1000
r2 = 2000
return
}
对于大量重复的参数类型 可以优化,放到最后一个参数声明即可
func foo4() (r1, r2 int) {
r1 = 3000
r2 = 4000
return // 返回的时候直接return就行,不需要加再加r1, r2
}
4. 导包
GO语言是以包为单位的,对于主文件来说,必须要有package main
,相当于main函数一样。对于其他文件包,只需要package 文件名
即可。
在Go语言入门环境搭建中有两个重要的概念GOPATH和GOROOT,其中GOROOT是我们安装的go路径例如D:\Go,将这个路径设置到环境变量就配置好了GO目录的安装路径了。PATH环境变量就是%GOROOT%\bin路径,如D:\Go\bin路径。
而GOPATH则是我们的工作目录,就是写代码的目录,在环境变量中配置之后,我们编译源代码等生成的文件都会放到这个目录下,这个目录下我们一般会新建三个文件夹:src、pkg、bin:
- src 目录存放的是我们的go源代码,不同工程项目的代码以包名区分。
- pkg 编译后生成的文件(比如:.a)
- bin文件夹存放go install命名生成的可执行文件,
Go.mod的好处
在开发中,我们可能会配置多个GOPATH,每个project使用不同的GOPATH。多个project会导致多个GOPATH目录,而go优先使用第一个GOPATH目录,会导致编译冲突,解决办法就是使用如下目录将多个project或工具组件都放在GOPATH的src目录下,此时GOBIN可以为空。GOPATH目录和GOPATH下的src不应该添加到源代码管理中,而是各个project目录myApp1、myApp2、myApp3各自独立进行源代码管理:
goWorkSpace // goWorkSpace为GOPATH目录
-- bin
-- myApp1 // 编译生成
-- myApp2 // 编译生成
-- myApp3 // 编译生成
-- pkg
-- src
-- common 1
-- common 2
-- common utils ...
-- myApp1 // project1
-- models
-- controllers
-- others
-- main.go
-- myApp2 // project2
-- models
-- controllers
-- others
-- main.go
-- myApp3 // project3
-- models
-- controllers
-- others
-- main.go
下面是几种导包的方式
同时,对于其他包来说,函数第一个字母必须大写,否则则是私有的,不能被外面使用。
5. 指针
和C/C++一样,GO的默认是值传递,同样也有指针,用法一样
func changeValue(p *int) {
*p = 10
}
func main() {
var a = 1
changeValue(&a)
fmt.Println("a = ", a)
}
6. defer
相当于C++的析构函数,在某个函数结束前执行的函数,defer关键字后面可以跟函数语句执行。注意:return先,defer后
func main() {
defer fmt.Println("main end1")
defer fmt.Println("main end2")
fmt.Println("main!")
}
defer会压栈,所以执行顺序是反的。
7. 动态数组
数组的定义
var myArray1 [10]int
myArray2 := [10]int{1,2,3,4}
for循环和C语言差不多,这里还有一个range,类似于python的enumerate
package main
import "fmt"
func main() {
var myArray1 [10]int
myArray2 := [10]int{1,2,3,4}
for i := 0; i < len(myArray1); i++ {
fmt.Println(myArray1[i], myArray2[i])
}
for index, value := range myArray2 {
fmt.Println("Index = ", index, "value = ", value)
}
}
注意,函数里面的数组传递,依旧是值传递,这一点和C不一样!
所以,我们需要修改数组的值的时候,需要用动态数组。动态数组的数组长度不能设置,必须是空
package main
import "fmt"
func printArray(myArray []int) { // 引用传递,不同元素长度的动态数组形参是一致的
for _, value := range(myArray) { // _代表匿名,访问不了
fmt.Println("value = ", value)
}
myArray[0] = 100
}
func main() {
myArray := []int{1,2,3,4} // 空心[]代表动态数组,切片,slice
fmt.Printf("type is %T\n", myArray)
printArray(myArray)
for _, value := range(myArray) { // _代表匿名,访问不了
fmt.Println("value = ", value)
}
}
若函数是动态数组方式,而你的数组又是固定长度的数组,你传参的时候需要这样:printArray(myArray2[:])
,变为引用传递,这样也能修改值
8. 切片
8.1 切片的定义
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
slice的4种定义方式
package main
import "fmt"
func main() {
slice1 := []int{1,2,3} // 声明slice1,并且初始化
var slice2 []int // 声明slice2是一个切片,但是没有分配空间
slice2 = make([]int, 3) // 分配空间,默认值是0
var slice3 []int = make([]int, 3)
slice4 := make([]int, 3) // 通过:=推导(常用方式)
fmt.Printf("len = %d, slice = %v\n", len(slice1), slice1)
fmt.Printf("len = %d, slice = %v\n", len(slice2), slice2)
fmt.Printf("len = %d, slice = %v\n", len(slice3), slice3)
fmt.Printf("len = %d, slice = %v\n", len(slice4), slice4)
}
8.2 切片的追加与截取
var slice3 []int = make([]int, 3, 5) // 设置容量为5
fmt.Printf("len = %d, slice = %v, cap = %d\n", len(slice3), slice3, cap(slice3))
追加
var slice3 []int = make([]int, 3, 5)
slice3 = append(slice3, 2)
fmt.Println("slice3 = %v", slice3) // 若超过容量了,容量会翻倍(与vector一致)
截取
这一块基本和python一致
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
以上结果为:
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]
8.3 append() 和 copy() 函数
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量 */
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
以上结果为:
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]
9. map
就是C中的map,键值对,底层是哈希表。当然,map也是引用传递。
package main
import (
"fmt"
)
func main() {
//第一种声明
var test1 map[string]string
//在使用map前,需要先make,make的作用就是给map分配数据空间
test1 = make(map[string]string, 10)
test1["one"] = "php"
test1["two"] = "golang"
test1["three"] = "java"
fmt.Println(test1) //map[two:golang three:java one:php]
//第二种声明
test2 := make(map[string]string)
test2["one"] = "php"
test2["two"] = "golang"
test2["three"] = "java"
fmt.Println(test2) //map[one:php two:golang three:java]
//第三种声明
test3 := map[string]string{
"one" : "php",
"two" : "golang",
"three" : "java",
}
fmt.Println(test3) //map[one:php two:golang three:java]
language := make(map[string]map[string]string)
language["php"] = make(map[string]string, 2)
language["php"]["id"] = "1"
language["php"]["desc"] = "php是世界上最美的语言"
language["golang"] = make(map[string]string, 2)
language["golang"]["id"] = "2"
language["golang"]["desc"] = "golang抗并发非常good"
fmt.Println(language) //map[php:map[id:1 desc:php是世界上最美的语言] golang:map[id:2 desc:golang抗并发非常good]]
//增删改查
// val, key := language["php"] //查找是否有php这个子元素
// if key {
// fmt.Printf("%v", val)
// } else {
// fmt.Printf("no");
// }
//language["php"]["id"] = "3" //修改了php子元素的id值
//language["php"]["nickname"] = "啪啪啪" //增加php元素里的nickname值
//delete(language, "php") //删除了php子元素
fmt.Println(language)
}