第一个go程序
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
- 入口是main函数,{ 括号必须和函数名同一行
- 每行结束不需要分号,需要对齐,保存时编译器自动对齐和删除分号
- fmt猜测是一个类,或者头文件,因为go是静态编译,fmt相关的都直接搬到该文件中
- go以package(包)为管理单位,必须有main包
不懂的地方
fmt是什么
package是什么
命令行运行和参数
go build xxx.go //生成.exe文件
go run xxx.go //直接运行,编译器也是直接运行
import (
"fmt"
"os"
)
func main() {
list := os.Args
fmt.Println(len(list))
for index, data := range list {
fmt.Printf("%d: %s\n", index, data)
}
}
- 和c的命令行参数类似,第一个是程序名字
变量声明
func main() {
var a, b int
fmt.Println("a =", a)
b = 10
fmt.Println("b =", b)
}
- 变量声明后必须使用,默认值为0;同理import "fmt",fmt也必须使用
- var 变量 数据类型
- 这里打印发现"a ="后面自动加了空格
不懂的地方
默认值是0怎么实现的?
变量初始化和auto
func main() {
var a int = 10
fmt.Println("a =", a)
b := 20
fmt.Printf("b type is %T", b)
}
- a初始化
- b用%T打印类型,:=是自动推导类型,类似c++的auto, 具体是先声明后赋值
- printf和c++的相似,而println是自动加换行
疑惑
Println具体实现是什么?
多重赋值
func main() {
a, b := 10, 20
a, b = b, a
fmt.Printf("a = %d, b = %d\n", a, b)
}
- 交换变量和python类似
疑惑
go的变量和python一样都是指针吗
const
func main() {
const a int = 10
const b = 20.2
fmt.Printf("a = %d, b type is %T", a, b)
}
- 注意const的自动类型推导不用:=,我想原因是:=是先声明后赋值,而const只能初始化,不能赋值
iota
func main() {
const (
a = iota
b, d = iota, iota
c = iota
)
fmt.Printf("a = %d, b = %d, c = %d, d = %d", a, b, c, d)
}
- a, b, d, c分别是0, 1, 1, 2,即从0开始++,同一行值一样
数据类型
- c的char在go是byte, 注意fmt.Println打印byte成int32
- bool, float32, float64, string, int
- complex128,复数
var a complex128 = 1.2 + 3.5i, 可以通过real()和imag()取实部和虚部
fmt
- %v,自动匹配格式,注意byte匹配成int32, %+v:显示详细信息(如打印结构体时可以用到)
- %T, 数据类型, 注意打印bool用的小写%t
- 输入:
scanf和scan,后者自动匹配格式,类似cin
类型转换
func main() {
var a bool = true
fmt.Printf("%d", a) //%!d(bool=true)
}
- int和bool不能相互转换
var a byte = 'c'
b := int(a)
- byte可以显式转换为int,go中不能隐式数据类型转换
类型别名
type myData int
var a myData = 10
fmt.Printf("a = %d", a)
- type和c的typedef类似
运算符
- 和c++类似,注意和python不同的是,拥有++和--运算符
- 没有?运算符
if, switch
func main() {
if b := 3; b == 3 {
fmt.Println("haha")
}
}
- 一个初始化语句(可以省略); 条件判断
- 注意判断没有()
- 和C相似,if...else if... else
- switch,默认加上了break,可以加
fallthrough即不break
循环
- for
func main() {
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
fmt.Println(sum)
}
- range
func main() {
var a string = "abcdef"
for index, data := range a { //两个返回值,下标和数据,类似于python的enumerate
fmt.Println(index, data)
}
for i := range a { //第二个默认丢弃,也可以清晰一点:for i, _ := range a
fmt.Printf("%d\n", i)
}
}
- 两个返回值,下标和数据,类似于python的enumerate
- 第二个默认丢弃,也可以清晰一点:for i, _ := range a
函数
- 支持多个返回值
- 返回值可以是函数
- 形参列表写成
a, b int表示a, b都是int类型,或者a int, b int
func compare(a, b int) (max, min int) {
if a >= b {
max, min = a, b
} else {
max, min = b, a
}
return max, min
}
func main() {
op1, op2 := compare(10, 20)
fmt.Println("max = ", op1, "min = ", op2)
}
疑惑
参数返回那个()作用是什么?
不定参数
func test(a int, args ...int) int { //返回类型是int
fmt.Println("len args = ", len(args)) // 3
return a
}
func main() {
var a int = test(10, 1, 2, 3)
fmt.Println("a = ", a)
}
- 类似python函数的*args, go用 name ...int表示
- 可以切片,如使用实参
args[:2]...
函数传递
type myFunc func(int, int) (int, int)
func main() {
var func1 myFunc
func1 = compare
op1, op2 := func1(10, 20)
fmt.Println("max = ", op1, "min = ", op2)
}
- 类似c++的函数指针
疑惑
内部如何实现, 函数指针的意义和实现方式,c++和go在这里的区别
匿名函数
func main() {
f1 := func(a, b int) (max, min int) {
if a >= b {
max = a
min = b
} else {
max = b
min = a
}
return max, min
}
op1, op2 := f1(10, 20)
fmt.Println("max = ", op1, "min = ", op2)
//2. 后面直接加()也是调用
op1, op2 = func(a, b int) (op1, op2 int) {
return a + b, a - b
}(10, 5)
fmt.Println("max = ", op1, "min = ", op2)
}
- 类似c++的lambda
闭包
- 在函数作用域外部可以对内部的变量操作,而函数内部也可以操作外部的变量,且变量长期在内存中
全是疑惑!!!不知道怎么使用
defer
func main() {
defer fmt.Println("over")
fmt.Println("begin")
}
- 类似于c++的析构函数,类释放前调用
- 若有多个defer,同继承类的析构函数,LIFO原则,最下面的最早调用
- 函数发生错误也会调用defer
工程管理
pkg, src, bin目录
- 这里需要了解!!!
- pkg放导入包,src放源程序,bin放.exe文件
- go install自动生成bin和pkg目录,需要设置GOBIN环境变量,需要了解!!!
导入包
import "fmt"import io "fmt"和python中import numpy as np一样- 同理还有
import . "fmt",那么fmt.Printf可以直接写为Printf,但这样名字冲突会出现 import _ "fmt",不用该包,只调用他的init函数- init函数:导入包的执行顺序:import "xxx" --> xxx的 func init --> 源文件的main
同级目录的.go文件
- package名字必须一样
- 可以直接调用另一个.go文件的函数
不同级目录
package main
import "fmt"
import "hello/src/hello/test" //包名和文件名是一样的
func main() {
a := test.Add(10, 20)
fmt.Println("a = ", a)
}
- package可以不同
- 调用另一个.go文件的函数:package.func()
- func首字母大写才能被其他文件调用,否则是私有的
疑惑
可以理解为package就是文件的声明吗?
其他数据类型
- 指针
- 数组
- 切片
- 字典
- 结构体
指针
- 同C, Printf用%p或%v打印&a的地址, Println自动类型
- 指针的数据类型为 int而不是 int
- 和C不同的是,空为nil, 没有->运算符,不支持指针运算
new
func main() {
var p *int = new(int)
*p = 100
fmt.Println(*p)
q := new(int)
*q = 200
fmt.Println(*q)
}
- 和C++类似
数组
var arr [10]int, 和C类似,只能用常量初始化数组大小for index, data := range arr常用遍历- 类似C++的列表初始化:
arr := [5]int{1, 2, 3},其他默认0;还可以设置位置:arr := [5]int{2: 30, 4:50} - 快速打印:用Println, 效果如:`[0 0 33 0 55]
- 二维数组:
arr := [3][4]int{1: {1, 2, 3, 4}}
fmt.Println(arr)
}
- 和vector类似,可以用==比较数组中每个元素是否都一样,=赋值
- !!!和C不同的是,函数传参时,形参数组是值传递,所以要用arr *[5]int, func(&arr)才能改变数组元素的值
随机数
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
for i := 0; i < 5; i++ {
k := rand.Intn(100) //左闭右开
fmt.Println(k)
}
}
疑惑
Seed中Unix()作用是什么
编译器提示现在不使用Seed了,需要改为什么
切片
- 用
a := []int{1, 2}, 或b := [...]int{2, 3}初始化 - 也可以用make初始化,
a := make([]int, len, cap), 其中cap可以省略 - [low: high: max], len = high - low, cap = max - low, 底层是原数组,同一个地址
slice = append(slice, val1, val2)在后面加入元素,自动扩容- 扩容特点和vector类似:
func main() {
arr := make([]int, 0, 1)
old_size := cap(arr)
for i := 0; i < 8; i++ {
fmt.Println(i, old_size)
arr = append(arr, 1)
if old_size < cap(arr) {
old_size = cap(arr)
}
}
}
- copy(s1, s2), 注意理解复制时的指针即可
- !!!函数传递切片是引用传递,和C的数组类似
疑惑
go的数组和切片作为参数传递时为什么不同,这样设计的意义在哪里
map,字典
- 无序,键值唯一
- 初始化:
hash := make(map[int]string, 10), 10是cap, 会自动扩容 hash := map[int]string{1: "tim", 2: "300"}也可以初始化- 遍历以及判断是否存在map中:
func main() {
//遍历
hash := map[int]string{1: "tim", 2: "300"}
for key, value := range hash {
fmt.Println(key, value)
}
//判断是否存在
val, exist := hash[1]
fmt.Println(val, exist) //false时,val为空字符串
}
- 删除:
delete(hash, 1),删除key值 - 作为函数参数:引用传递
结构体
type student struct {
name string
score int
}
func main() {
var s1 student = student{"tim", 90}
fmt.Println(s1)
s2 := student{name: "amy"}
fmt.Println(s2)
}
- 结构体初始化如上代码块
- 结构体指针:
s2 := &student{name: "amy"}
fmt.Println(*s2)
- 结构体指针访问用.和(*p).效果一样,
fmt.Println(s2.name) - 函数传递结构体时是值传递,所以需要改变里面内容时要加*和&
- 补充:public和privated在go里通过不同package里面的首字母判断,首字母大写是public,反之privated