golang入门学习

准备入坑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
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
   /* 这是我的第一个简单的程序 */
   fmt.Println("Hello, World!")
}
  1. 第一行代码 定义了包名,package main表示一个可以独立执行的程序,每个Go应用程序都包含一个名为main的包
  2. import “fmt”告诉编译器这个程序需要fmt包
  3. func main()是程序开始执行的函数,main函数是每一个可执行程序必须包含的,一般来说都是启动后第一个执行的函数,如果有init()函数则先执行这个
  4. fmt.Println是一个函数
  5. 当标识符(包括常量、变量、类型、函数名、结构字段等)以一个大写字母开头,如Group1,那么这种标识符的对象可以被外部包的代码所使用,客户端程序需要先导入这个包,被称为导出(类似public),如果小写字母开头则不可见,但是在整个包的内部是可见的,可用的(protected)

执行Go程序

1
2
3
4
5
6
7
8
// hello.go
package main

import "fmt"

func main() {
fmt.Println("hello world!")
}
1
2
3
go run hello.go
||
go build hello.go&&./hello

“{”不能单独放一行

Go语言基础语法

行分隔符

在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。

如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。

1
2
fmt.Println("Hello, World!")
fmt.Println("菜鸟教程:runoob.com")

字符串连接

可以使用加号实现

1
2
3
4
5
package main
import "fmt"
func main() {
    fmt.Println("Google" + "Runoob")
}

空格

  • Go 语言中变量的声明必须使用空格隔开,如:

    1
    2
    var x int
    const Pi float64 = 3.14159265358979323846
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package 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
    3
    if x > 0 {
    // do something
    }
  • 格式化字符串

    Go 语言中使用 fmt.Sprintffmt.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-31
    1
    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
    2
    var vname1, vname2, vname3 = v1, v2, v3
    vname1, vname2, vname3 := v1, v2, v3

值类型和引用类型

  • 值类型
    1. 当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝
    2. 所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
    3. 可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040

语言常量

const identifier [type] = value

常量也可以用于枚举:

1
2
3
4
5
const (
Unknown = 0
Female = 1
Male = 2
)

iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量。

iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

iota 可以被用作枚举值:

1
2
3
4
5
const (
a = iota
b = iota
c = iota
)

第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:

1
2
3
4
5
const (
a = iota
b
c
)

运算符

和C一样

条件语句

  • if

    1. 不需要用括号把条件包起来
    2. 大括号必须存在
    3. 在if之后条件语句之前可以用变量初始化语句,用;分隔
    4. 在有返回值的函数中,最终的return不能出现在条件语句中
  • if else

  • if 嵌套

  • switch

    1
    2
    3
    4
    5
    6
    7
    8
    switch 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
    25
    package 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
    3
    2、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
    3
    for 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
    11
    package 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
    17
    package 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
2
3
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
    25
    package 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
2
3
4
5
6
7
8
/* 定义交换值函数*/
func swap(x *int, y *int) {
var temp int
temp = *x /* 保持 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}

函数用途

  • 作为实参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
   "fmt"
   "math"
)

func main(){
   /* 声明函数变量 */
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }

   /* 使用函数 */
   fmt.Println(getSquareRoot(9))

}
  • 函数方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package 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
    2
    var numbers = [5]int{1, 2, 3, 4, 5}
    numbers := [5]int{1, 2, 3, 4, 5}

    注意:在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,也就是说 [5]int[10]int 是不同的类型。

    如果数组长度不确定,可以使用 代替数组的长度,编译器会根据元素个数自行推断数组的长度:

    1
    2
    3
    var 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
    // Step 1: 创建数组
    values := [][]int{}

    // Step 2: 使用 append() 函数向空的二维数组添加两行一维数组
    row1 := []int{1, 2, 3}
    row2 := []int{4, 5, 6}
    values = append(values, row1)
    values = append(values, row2)

    // Step 3: 显示两行数据
    fmt.Println("Row 1")
    fmt.Println(values[0])
    fmt.Println("Row 2")
    fmt.Println(values[1])

    // Step 4: 访问第一个元素
    fmt.Println("第一个元素为:")
    fmt.Println(values[0][0])
}

向函数传递数组

可以设置或者不设置形参大小:

1
2
3
4
5
6
7
func myFunction(param [10]int) {
....
}
func myFunction(param []int) {
....
}

如果你想要在函数内修改原始数组,可以通过传递数组的指针来实现。

以下实例演示如何向函数传递数组,函数接受一个数组和数组的指针作为参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

// 函数接受一个数组作为参数
func modifyArray(arr [5]int) {
    for i := 0; i < len(arr); i++ {
        arr[i] = arr[i] * 2
    }
}

// 函数接受一个数组的指针作为参数
func modifyArrayWithPointer(arr *[5]int) {
    for i := 0; i < len(*arr); i++ {
        (*arr)[i] = (*arr)[i] * 2
    }
}

func main() {
    // 创建一个包含5个元素的整数数组
    myArray := [5]int{1, 2, 3, 4, 5}

    fmt.Println("Original Array:", myArray)

    // 传递数组给函数,但不会修改原始数组的值
    modifyArray(myArray)
    fmt.Println("Array after modifyArray:", myArray)

    // 传递数组的指针给函数,可以修改原始数组的值
    modifyArrayWithPointer(&myArray)
    fmt.Println("Array after modifyArrayWithPointer:", myArray)
}

指针

1
2
var ip *int        /* 指向整型*/
var fp *float32 /* 指向浮点型 */
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package 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
2
3
4
5
var slice1 []type = make([]type, len)

也可以简写为

slice1 := 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 main

    import “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")