golang入门学习
golang入门学习
Hoshea Zhang准备入坑6.824,因此先学习一下golang
不分多篇文章了,就在这一章节去讲
环境搭建
简要讲一下,是在linux上搭建的,下载go的压缩包,解压后设置环境变量,然后就可以go version
了
第一个问题:我在vscode上跑的,很多组件安装不起来,需要在go env
命令中看一下gopath,然后创建三个路径:src pkg bin
第二个问题: 需要创建一个go.mod
,具体方法百度一下,如果会报错可以看下面这个网址:
https://blog.csdn.net/u011285281/article/details/131261615
Go语言结构
Go 语言的基础组成有以下几个部分:
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
1 | package main |
- 第一行代码 定义了包名,package main表示一个可以独立执行的程序,每个Go应用程序都包含一个名为main的包
import “fmt”
告诉编译器这个程序需要fmt包- func main()是程序开始执行的函数,main函数是每一个可执行程序必须包含的,一般来说都是启动后第一个执行的函数,如果有init()函数则先执行这个
fmt.Println
是一个函数- 当标识符(包括常量、变量、类型、函数名、结构字段等)以一个大写字母开头,如Group1,那么这种标识符的对象可以被外部包的代码所使用,客户端程序需要先导入这个包,被称为导出(类似public),如果小写字母开头则不可见,但是在整个包的内部是可见的,可用的(protected)
执行Go程序
1 | // hello.go |
1 | go run hello.go |
“{”不能单独放一行
Go语言基础语法
行分隔符
在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。
1 | fmt.Println("Hello, World!") |
字符串连接
可以使用加号实现
1 | package main |
空格
Go 语言中变量的声明必须使用空格隔开,如:
1
2var x int
const Pi float64 = 3.141592653589793238461
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package main
import "fmt"
func main() {
var x int = 20
const Pi float64 = 3.1415
fmt.Println(x)
fmt.Println(Pi)
}
/*
20
3.1415
*/在关键字和表达式之间要使用空格。
例如:
1
2
3if x > 0 {
// do something
}格式化字符串
Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
- Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
- Printf 根据格式化参数生成格式化的字符串并写入标准输出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//sprintf
package main
import (
"fmt"
)
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-311
2
3
4
5
6
7
8
9
10
11
12
13
14//printf
package main
import (
"fmt"
)
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
fmt.Printf(url,stockcode,enddate)
}
数据类型
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型(c) 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型 |
语言变量
一般形式为var identifier type
声明
指定变量类型
如果没有初始化,变量默认为0值
根据值自行判断变量类型
var v_name = value
如果变量已经使用var声明过了,再用
:=
声明,会出现编译错误声明并赋值
多变量声明
1
2var vname1, vname2, vname3 = v1, v2, v3
vname1, vname2, vname3 := v1, v2, v3
值类型和引用类型
- 值类型
- 当使用等号
=
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i 的值进行了拷贝 - 所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
- 可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040
- 当使用等号
语言常量
const identifier [type] = value
常量也可以用于枚举:
1 | const ( |
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值:
1 | const ( |
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
1 | const ( |
运算符
和C一样
条件语句
if
- 不需要用括号把条件包起来
- 大括号必须存在
- 在if之后条件语句之前可以用变量初始化语句,用
;
分隔 - 在有返回值的函数中,最终的return不能出现在条件语句中
if else
if 嵌套
switch
1
2
3
4
5
6
7
8switch var1 {
case val1:
...
case val2:
...
default:
...
}switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package main
import "fmt"
func main() {
switch {
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")
case false:
fmt.Println("5、case 条件语句为 false")
fallthrough
default:
fmt.Println("6、默认 case")
}
}1
2
32、case 条件语句为 true
3、case 条件语句为 false
4、case 条件语句为 true
循环语句
for
和C的for一样:
1
for init; condition; post { }
和C的while一样
1
for condition { }
和C的for {;; }一样
1
for { }
for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:
1
2
3for key, value := range oldMap {
newMap[key] = value
}以上代码中的 key 和 value 是可以省略。
如果只想读取 key,格式如下:
1
for key := range oldMap
或者这样:
for key, _ := range oldMap
如果只想读取 value,格式如下:
1
for _, value := range oldMap
例子:
1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func main() {
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Println(sum)
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package main
import "fmt"
func main() {
sum := 1
for ; sum <= 10; {
sum += sum
}
fmt.Println(sum)
// 这样写也可以,更像 While 语句形式
for sum <= 10{
sum += sum
}
fmt.Println(sum)
}
函数定义
1 | func function_name( [parameter list] ) [return_types] { |
示例和调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package main
import (
"fmt"
)
func main() {
var a int = 100
var b int = 200
var ret int
ret = max(a, b)
fmt.Printf("最大值为:%d\n", ret)
}
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
return result
}
引用传递
引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
引用传递指针参数传递到函数内,以下是交换函数 swap() 使用了引用传递:
1 | /* 定义交换值函数*/ |
函数用途
- 作为实参
1 | package main |
函数方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
import (
"fmt"
)
/* 定义结构体 */
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
}
数组
声明
1
var arrayName [size]dataType
初始化
1
2var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}注意:在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,也就是说 [5]int 和 [10]int 是不同的类型。
如果数组长度不确定,可以使用 … 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
1
2
3var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
多维数组
1 | var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type |
1 | package main |
向函数传递数组
可以设置或者不设置形参大小:
1 | func myFunction(param [10]int) { |
如果你想要在函数内修改原始数组,可以通过传递数组的指针来实现。
以下实例演示如何向函数传递数组,函数接受一个数组和数组的指针作为参数:
1 | package main |
指针
1 | var ip *int /* 指向整型*/ |
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
切片(动态数组)
创建
1 | var slice1 []type = make([]type, len) |
切片不需要指定长度,第三个参数capacity指定容量为可选参数:
1 | s :=[] int {1,2,3 } |
map
定义
1
2/* 使用 make 函数 */
map_variable := make(map[KeyType]ValueType, initialCapacity)1
2
3
4
5// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)```go
package mainimport “fmt”
func main() {
var siteMap map[string]string /*创建集合 */ siteMap = make(map[string]string) /* map 插入 key - value 对,各个国家对应的首都 */ siteMap [ "Google" ] = "谷歌" siteMap [ "Runoob" ] = "菜鸟教程" siteMap [ "Baidu" ] = "百度" siteMap [ "Wiki" ] = "维基百科" /*使用键输出地图值 */ for site := range siteMap { fmt.Println(site, "首都是", siteMap [site]) } /*查看元素在集合中是否存在 */ name, ok := siteMap [ "Facebook" ] /*如果确定是真实的,则存在,否则不存在 */ /*fmt.Println(capital) */ /*fmt.Println(ok) */ if (ok) { fmt.Println("Facebook 的 站点是", name) } else { fmt.Println("Facebook 站点不存在") }
}
1
Wiki 首都是 维基百科
Google 首都是 谷歌
Runoob 首都是 菜鸟教程
Baidu 首都是 百度
Facebook 站点不存在1
2
3
4
5
6
7
- 删除
delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:
```go
delete(countryCapitalMap, "France")