Go语言学习第一天
package main import "fmt" func main() { /* 这是我的第一个简单的程序 */ fmt.Println("Hello, World!") }
- 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
-
下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)
文件结构
文件名与包名没有直接关系,不一定要将文件名与包名定成同一个文件夹名与包名没有直接关系,并非需要一致。同一个文件夹下的文件只能有一个包名,否则编译报错:= 声明变量v_name := value
var intVal int intVal =1
//类型相同多个变量, 非全局变量 var vname1, vname2, vname3 type vname1, vname2, vname3 = v1, v2, v3 var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断 vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误 // 这种因式分解关键字的写法一般用于声明全局变量 var ( vname1 v_type1 vname2 v_type2 )
变量的生命周期
全局变量生命周期是程序存活时间局部变量是函数存活时间var dic = map[string]int{ "apple": 1, "banana": 2, } if num, ok := dic["orange"]; ok { fmt.Printf("orange num %d\n", num) } if num, ok := dic["banana"]; ok { fmt.Printf("banana num %d\n", num) } switch localStr { case "case1": fmt.Println("case1") default: fmt.Println("default") }
数组
var strAry = [10]string{"aa", "bb", "cc", "dd", "ee"} //切片初始化 var sliceAry = make([]string, 0) sliceAry = strAry[1:3] fmt.Printf("strAry %+v\n", strAry) fmt.Printf("sliceAry %+v\n", sliceAry) //字典初始化
//字典初始化
var dic = map[string]int{
"apple": 1,
"watermelon": 2,
}
fmt.Printf("dic %+v\n", dic)循环
package main import "fmt" func main() { for i := 0; i < 5; i++ { fmt.Printf("current i %d\n", i) } j := 0 for { if j == 5 { break } fmt.Printf("current j %d\n", j) j++ } var strAry = []string{"aa", "bb", "cc", "dd", "ee"} //是的,不指定初始个数也ok //切片初始化 var sliceAry = make([]string, 0) sliceAry = strAry[1:3] for i, str := range sliceAry { fmt.Printf("slice i %d, str %s\n", i, str) } //字典初始化 var dic = map[string]int{ "apple": 1, "watermelon": 2, } for k, v := range dic { fmt.Printf("key %s, value %d\n", k, v) } }
for rang的坑
arr := [2]int{1, 2} res := []*int{} for _, v := range arr { res = append(res, &v) }fmt.Println(*res[0],*res[1]) //expect: 1 2 fact: 2 2
内部调用还是for循环,初始化会拷贝带遍历的列表(如array,slice,map),然后每次遍历的v
都是对同一个元素的遍历赋值。 也就是说如果直接对v
取地址,最终只会拿到一个地址,而对应的值就是最后遍历的那个元素所附给v
的值。对应伪代码如下// len_temp := len(range) // range_temp := range // for index_temp = 0; index_temp < len_temp; index_temp++ { // value_temp = range_temp[index_temp] // index = index_temp // value = value_temp // original body // }
解决方案
//1局部变量拷贝 for _, v := range arr {//局部变量v替换了v,也可用别的局部变量名 v1 := v res = append(res, &v1) }
for k := range arr { res = append(res, &arr[k]) }
用鹏哥的话就是arr[:] 类似于切片截取,当没有参数的时候,就是截取整个原数组,返回的结果和原切片其实是指向同一个底层数组,指针不同而已。总结一下,就是切片结构体指针类型,指向原底层数组,返回一个新的指针。
对大数组这样遍历有什么问题?
//假设值都为1,这里只赋值3个 var arr = [204800]int{1, 1, 1} for i, n := range arr { _ = i _ = n }
第一种:对数组取地址遍历for i, n := range &arr
,因为数组去的是地址,而不是整个数组,所以在拷贝的时候是拷贝的地址,比整个数组要小得多。第二种:对数组做切片引用for i, n := range
arr[:]
在for range中这样起goroutine能得到想要的结果么
var m = []int{1, 2, 3} for i := range m { go func() { fmt.Print(i) }() } //阻塞1分钟等待所有goroutine运行完 time.Sleep(time.Millisecond)
因为很有可能当for循环执行完之后,goroutine才开始执行,这个时候val的值是指向了切片中最后一个元素。所以三个goroutine打印出来的结果相同for i := range m { go func(i int) { fmt.Print(i) }(i) }