基础
# 基础
Owner: -QVQ-
# 基础
# go的编写
linux下运行go文件:go run *.go
生成二进制文件: go build *.go
//必须在非注释的第一行指明这个文件属于哪个包
package main//定义了包名,每个应用程序都包含一个main的包
import "fmt"//需要使用fmt包,包含格式化IO的函数
//启动后的第一个执行函数(如果有init函数就先执行init)
func main() {//这个大括号必须放在这里,不能单独一行
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}
2
3
4
5
6
7
8
9
输出:
fmt.Print(”hello,world\n”)
fmt.Println(”hello,world”)
//输出并自动加上换行符\n
上面两句效果一样
上面两个函数均支持变量
标识符的定义:
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么这个对象可以被外部包使用(客户端程序需要先导入这个包(导出))
标识符以小写字母开头则智能在包的内部可见
命名规则同c
包:
文件名、文件夹名、包名并没有关系可以都不同?
同一文件夹下多个go文件必须是同一个包名
在vscode中运行如下命令将当前文件夹配置成一个go的包,从而在vscode命令行中输入go run
运行
go mod init gitee.com/mall_lucy/my_go_studycode
同一个包下的编写:
/*-----hello.go------*/
package main
func main() {
Add(7, 8)
}
/*-----func.go------*/
package main
import "fmt"//这个文件包含了可以调用,同一个包下多文件不共享
func Add(a int, b int) {//这里必须是大写开头不然其他文件调不到
fmt.Println(a, b)
}
//用go run .运行
2
3
4
5
6
7
8
9
10
11
12
13
14
15
代码编写:
行分隔符;
可以将多个语句写在同一行
注释、标识符同c一样
变量的声明间必须用空格隔开,变量的操作不用
var x int
在关键字和表达式之间要使用空格。
if x > 0 {}
函数调用时,函数名和等号有空格,参数间有空格
result := add(2, 3)
字符串连接
fmt.Println("Google" + "Runoob")
//输出GoogleRunoob
# 变量
# 格式化字符串
• Sprintf 根据格式化参数生成格式化的字符串,返回该字符串。
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
var target_url=fmt.Sprintf(url,stockcode,enddate)
fmt.Println(target_url)
}//输出:Code=123&endDate=2020-12-31
2
3
4
5
6
7
8
• Printf 根据格式化参数生成格式化的字符串,写入标准输出。
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
fmt.Printf(url,stockcode,enddate)
}//输出:Code=123&endDate=2020-12-31
2
3
4
5
6
7
%T | 输出变量的类型 |
---|---|
# 数据结构
- 布尔型
var b bool = true
数字类型
浮点数和复数
1 | float32 IEEE-754 32位浮点型数 |
---|---|
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
整型
1 | uint8 无符号 8 位整型 (0 到 255) |
---|---|
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 |
其他数字类型
1 | byte 类似 uint8 |
---|---|
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
- 字符串类型
**var** a string = "Runoob"
- 派生类型
(a) 指针类型(Pointer)
var a *int
(b) 数组类型
var a []int
(c) 结构化类型(struct)
(d) Channel 类型
var a chan int
(e) 函数类型
var a func(string) int
(f) 切片类型
(g) 接口类型(interface)
var a error // error 是接口
(h) Map 类型
var a map[string] int
# 变量
变量声明:
var identifier type
//指定变量类型
var v_name = value
//自行判别变量类型
v_name := value
//同上,这是一个声明语句
例子
**var** a string = "Runoob"
**var** b, c int = 1, 2
如果没有初始化,默认为0值
多变量声明:
var vname1, vname2, vname3 type
//同类变量一次定义
vname1, vname2, vname3 = v1, v2, v3
//多变量赋值
var vname1, vname2, vname3 = v1, v2, v3
//自动推断类型
vname1, vname2, vname3 := v1, v2, v3
// 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
2
3
4
5
例子
package main
import "fmt"
var x, y int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
//这种不带声明格式的只能在函数体中出现
//g, h := 123, "hello"
func main(){
g, h := 123, "hello"//可以高效的创建变量,初始化声明
fmt.Println(x, y, a, b, c, d, e, f, g, h)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意: 函数内的变量一旦定义,必须使用,不然会编译报错 全局变量没有限制 **
var** a string = "abc"
这样的赋值操作不算使用
一些细节:
a, b, c = 5, 7, "abc"
//这种写法假设了abc都已声明
如果部分未声明,可以如下这样写,称为并行 或 同时 赋值。
a, b, c := 5, 7, "abc"
//a声明了,bc没有声明
a, b = b, a
//交换两个变量
val, err = Func1(var1)
//并行赋值也可用于一个函数的多个返回值
空白标识符_
用于抛弃值,实际上是个只写变量,不能得到它的值
_, b = 5, 7
//5被抛弃
# 值类型和引用类型
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
使用等号 =
将一个变量的值进行了一次拷贝
值类型变量的值存储在堆中
&i
获取变量i的内存地址
引用类型的变量 r1,即指针
# 常量
常量中的数据类型只可以是布尔型、数字型和字符串型。
const identifier [type] = value
- 显式类型定义:
const b string = "abc"
- 隐式类型定义:
const b = "abc"
常量用于枚举
const (
Unknown = 0
Female = 1
Male = 2
)
2
3
4
5
给一个赋值其他会自动赋值
const (
a1 = 1
a2
a3
)//此时三个都是1
2
3
4
5
const (
a1 = 1
a2 = 2
a3
)//此时分别为1 2 2
2
3
4
5
常量可以结合函数计算出值,函数必须是内置函数
package main
import "unsafe"
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main(){
println(a, b, c)//输出 abc 3 16
}
2
3
4
5
6
7
8
9
10
11
12
iota 特殊常量(仅可被编译器修改)
iota在const关键字出现时被重置为0,之后每新增一行常量声明,iota的值自动+1
属于const类型,所以只能赋值给常量
const (
a = iota
b = iota
c
)//此时分别是0 1 2
2
3
4
5
貌似iota在不同函数域下单独计算
const (
a1 = iota
a2 = 2
a3
)
func main() {
fmt.Println(a1, a2, a3)//0 2 2
const b = iota
fmt.Println(b)// 0
}
2
3
4
5
6
7
8
9
10
const的自动赋值是包含了运算的
const (
i=1<<iota
j=3<<iota
k
l
)
func main() {
fmt.Println("i=",i)//1
fmt.Println("j=",j)//6
fmt.Println("k=",k)//12
fmt.Println("l=",l)//24
}//3<<3 == 3 * 2^3 == 24
2
3
4
5
6
7
8
9
10
11
12
13
# 变量作用域
- 函数内定义的变量称为局部变量(包含返回值)
- 函数外定义的变量称为全局变量(可能在外部包使用)
- 函数定义中的变量称为形式参数(即函数值传入的参数)
# go语言运算符
全部同c
越往上优先级越高
5 | * / % << >> & &^ |
---|---|
4 | + - |
3 | == != < <= > >= |
2 | && |
1 |
# 控制
# 条件判断
无三目运算符
# if语句
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}//支持else if
2
3
4
5
# switch语法
支持常量和变量的混合比较,但必须是同一类型
switch var1 {
case val1:
...
case val2:
...
default:
...
}
2
3
4
5
6
7
8
例子:
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"//支持多个同类
default: grade = "D"
}
2
3
4
5
6
# type switch 判断变量中的类型
switch x.(type){
case type:
statement(s); //不需要break分割,每个case只执行一个
case type:
statement(s);
/* 你可以定义任意个数的case */
default: /* 可选 */
statement(s);
}
2
3
4
5
6
7
8
9
例子:
var x interface{}
switch x.(type) {
case nil:
fmt.Printf(" x 的类型 :nil")
case int:
fmt.Printf("x 是 int 型")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型" )
default:
fmt.Printf("未知型")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
结合fallthrough
强制执行下一条case语句(执行多个case的方法)
switch {//当这里什么都不填默认为true
case false:
fmt.Println("1、case 条件语句为 false")
fallthrough
case true:
fmt.Println("2、case 条件语句为 true")
fallthrough
case false:
fmt.Println("3、case 条件语句为 false")
fallthrough
case true:
fmt.Println("4、case 条件语句为 true")//这里没有fallthrough停下来
case false:
fmt.Println("5、case 条件语句为 false")
fallthrough
default://这里的default不论放在前后都是最后执行
fmt.Println("6、默认 case")
}
//输出:
//2、case 条件语句为 true
//3、case 条件语句为 false
//4、case 条件语句为 true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
可以在switch中结合break终止执行
# select语法
只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收
如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。
select {
case <- channel1:
// 执行的代码
case value := <- channel2:
// 执行的代码
case channel3 <- value:
// 执行的代码
// 你可以定义任意数量的 case
default:
// 所有通道都没有准备好,执行的代码
//如果没有default语句将阻塞,直到某个通道可以运行
}
2
3
4
5
6
7
8
9
10
11
12
13
14
所有channel表达式都会被求值,go不会重新对channel求值
例子
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}//执行结果
//received one
//received two
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
注意select中的break并不能直接跳出select语句,只是结束当前的循环,如果要跳出select,需要用return或者goto
# 循环语句
# for循环
for init; condition; post { }
//同c的for
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
2
3
4
for condition { }
//同c的while
for { }
//同for(;;)无限循环
for的range格式
可以对 slice、map、数组、字符串等进行迭代循环
for key, value := range oldMap {
newMap[key] = value
}
//for key := range oldMap //读取部分数据
//for _, value := range oldMap //读取部分数据
2
3
4
5
# continue关键字、break关键字
不使用标记则结束最近的一个循环,使用标记re
则指定结束的一轮循环位置
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
continue re
}
}
//输出
//i: 1
//i2: 11
//i: 2
//i2: 11
//i: 3
//i2: 11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
break同理
# goto关键字
goto label;
..
.
label: statement;
2
3
4
# 函数
# 函数基础
func function_name( [parameter list] ) [return_types] {
函数体
}
2
3
**函数的参数:**不允许全部不给定类型,前面的参数没有给类型,会自动和后面给定了类型的参数一个类型
**函数的返回值:**可以有多个返回值
函数的参数分为值传递和引用传递,同c
例子
swap(&a, &b)
func swap(x *int, y *int){}
//好像没有func swap(x &int, y &in)
2
3
声明一个函数类型:
type cb func(int) int
//
# 函数作为实参、实现回调
// 声明一个函数类型
type cb func(int) int
func main() {
testCallBack(1, callBack)//进行回调
}
//回调函数
func testCallBack(x int, f cb) {//这个f是一类的函数,可以用于实现多态
f(x)
}
//主函数
func callBack(x int) int {
fmt.Printf("我是回调,x:%d\n", x)
return x
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 闭包
方式一:
getSquareRoot := func(x float64) float64 {
return x*x
}//声明函数变量,相当于函数指针
fmt.Println(getSquareRoot(9)) /* 使用函数 */
2
3
4
方式二:
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
func main(){
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber := getSequence()
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber())//输出 1
fmt.Println(nextNumber())//输出 2
fmt.Println(nextNumber())//输出 3
/* 创建新的函数 nextNumber1,并查看结果 */
nextNumber1 := getSequence()
fmt.Println(nextNumber1())//输出 1
fmt.Println(nextNumber1())//输出 2
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 方法
包含了接收者的函数
接受者可以是命名类型或者结构体类型的一个值或者是一个指针
所有给定类型的方法属于该类型的方法集。
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
2
3
例子
/* 定义结构体 */
type Circle struct {
radius float64
}
func main() {
var c1 Circle
c1.radius = 10.00
fmt.Println("圆的面积 = ", c1.getArea())
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
//c.radius 即为 Circle 类型对象中的属性
return 3.14 * c.radius * c.radius
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Go错误处理
error 类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
2
3
使用:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
}
2
3
4
5