Go 语言之基础语法3 | 青训营笔记

67 阅读8分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

上一篇主要介绍了 Go 语言的条件、循环语句以及函数的相关知识,本章将针对 Go 语言中的部分数据结构进行介绍。

01. Go 语言数组

  • 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型,例如整型、字符型或者自定义类型。
  • 使用数组形式更加方便且易于扩展
/* 使用数组 声明变量number0, number1, ... , number99 */
var number [99] variable_type  // variable_type 声明数组中的元素类型
  • 数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为 0,第二个索引为 1,以此类推

数组索引.jpg

1.1 声明数组

Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:

var variable_name [SIZE] variable_type 

数组长度必须是整数且大于 0

1.2 初始化数组

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 
  • 初始化数组中 {} 中的元素个数不能大于 [] 中的数字

  • 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小

 var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}

与 C 语言类似,Go 语言也同样支持多维数组,具体介绍可参考 C语言中文网 对 Go 语言多维数组简述

0.2 Go 语言指针

Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务。我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。Go 语言的取地址符&放到一个变量前使用就会返回相应变量的内存地址

2.1 什么是指针?

一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址。类似于变量和常量,在使用指针前你需要声明指针。

指针声明格式如下:

var var_name *var-type
var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。

以下是有效的指针声明

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */

2.2 使用指针的流程

  • 定义指针变量
  • 为指针变量赋值
  • 访问指针变量中指向地址的值

在指针类型前面加上*号(前缀)来获取指针所指向的内容

/* 实例 */
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 )
}
/* 
    执行结果:
    a 变量的地址是: 20818a220
    ip 变量的存储地址: 20818a220
    *ip 变量的值: 20
*/

2.3 空指针

  • 当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值

  • 一个指针变量通常缩写为 ptr

/* 实例 */
package main

import "fmt"

func main() {
   var  ptr *int
   fmt.Printf("ptr 的值为 : %v\n", ptr  )
   fmt.Printf("ptr 的值为 : %#v\n", ptr  )

}
/*
    输出结果:
    ptr 的值为 : <nil>
    ptr 的值为 : (*int)(nil)
*/

空指针判断

/* 实例 */
if(ptr != nil)     /* ptr 不是空指针 */
if(ptr == nil)    /* ptr 是空指针 */

03. Go 语言结构体

  • Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
  • 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合
  • 结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:
    • Title :标题
    • Author : 作者
    • Subject:学科
    • ISBN:书籍ID

04. Go 语言切片(Slice)

  • Go 语言切片是对数组的抽象
  • Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

4.1 定义切片

你可以声明一个未指定大小的数组来定义切片:

var identifier []type

切片不需要说明长度。

或使用 make()函数 来创建切片:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

也可以指定容量,其中capacity为可选参数

make([]T, length, capacity)

这里len 是数组的长度并且也是切片的初始长度

4.2 len() 和 cap() 函数

切片是可索引的,并且可以由len()方法获取长度

切片提供了计算容量的方法cap()可以测量切片最长可以达到多少

4.3 切片截取

可以通过设置下限及上限来设置截取切片[lower-bound:upper-bound]

/* 实例 */
func main() {
   /* 创建切片 */
   numbers := []int{0,1,2,3,4,5,6,7,8}   
   printSlice(numbers)

   /* 打印原始切片 */
   fmt.Println("numbers ==", numbers)

   /* 打印子切片从索引1(包含) 到索引4(不包含)*/
   fmt.Println("numbers[1:4] ==", numbers[1:4])

   /* 默认下限为 0*/
   fmt.Println("numbers[:3] ==", numbers[:3])

   /* 默认上限为 len(s)*/
   fmt.Println("numbers[4:] ==", numbers[4:])

   numbers1 := make([]int,0,5)
   printSlice(numbers1)

   /* 打印子切片从索引  0(包含) 到索引 2(不包含) */
   number2 := numbers[:2]
   printSlice(number2)

   /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
   number3 := numbers[2:5]
   printSlice(number3)
}

4.4 append() 和 copy() 函数

如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来

/* 实例 */
func main() {
   var numbers []int
   printSlice(numbers)

   /* 允许追加空切片 */
   numbers = append(numbers, 0)
   printSlice(numbers)

   /* 向切片添加一个元素 */
   numbers = append(numbers, 1)
   printSlice(numbers)

   /* 同时添加多个元素 */
   numbers = append(numbers, 2,3,4)
   printSlice(numbers)

   /* 创建切片 numbers1 是之前切片的两倍容量*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* 拷贝 numbers 的内容到 numbers1 */
   copy(numbers1,numbers)
   printSlice(numbers1)   
}

若只看实例代码不易理解,可参考 Go 语言中文标准库

04. Go 语言范围(Range)

  • Go 语言中 range 关键字用于for循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引值,在集合中返回 key-value 对的 key 值。
  • 对于一个切片(slice)或者一个集合(map)的话,我们可以用 range 来快速遍历,这样代码能够更加简洁。range遍历的时候,对于数组会返回两个值,第一个是索引,第二个是对应位置的值。如果我们不需要索引的话,我们可以用下划线来忽略。
/* 实例 */
func main() {
    //这是我们使用range去求一个slice的和。使用数组跟这个很类似
    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)
    //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }
    //range也可以用在map的键值对上。
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }
    //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
    for i, c := range "go" {
        fmt.Println(i, c)
    }
}
/*
    执行结果:
    sum: 9
    index: 1
    a -> apple
    b -> banana
    0 103
    1 111
*/

05. Go 语言Map(集合)

  • Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

  • Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

5.1 定义 Map

可以使用内建函数 make 也可以使用 map 关键字来定义 Map

/* 实例 */
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */
map_variable = make(map[key_data_type]value_data_type)

如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对

5.2 delete() 函数

delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key

/* 实例 */
func main() {   
   /* 创建 map */
   countryCapitalMap := map[string] string {"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New Delhi"}
   
   fmt.Println("原始 map")   
   
   /* 打印 map */
   for country := range countryCapitalMap {
      fmt.Println("Capital of",country,"is",countryCapitalMap[country])
   }
   
   /* 删除元素 */
   delete(countryCapitalMap,"France");
   fmt.Println("Entry for France is deleted")  
   
   fmt.Println("删除元素后 map")   
   
   /* 打印 map */
   for country := range countryCapitalMap {
      fmt.Println("Capital of",country,"is",countryCapitalMap[country])
   }
}
    /*
    执行结果:
    原始 map
    Capital of France is Paris
    Capital of Italy is Rome
    Capital of Japan is Tokyo
    Capital of India is New Delhi
    Entry for France is deleted
    删除元素后 map
    Capital of Italy is Rome
    Capital of Japan is Tokyo
    Capital of India is New Delhi
    */

对于C、Java、Go而言,可能第一感觉就是 C++ 吃力不讨好,可能 C++ 和 Java 、前端、安卓同样的工资待遇,但是干 C++的心更累。Java真的很不错,有时候找不出什么出挑的毛病。有一些不太好的地方,但都不是实质性问题。总得来说,语言只是工具,能正确使用工具,解决问题就好。但是解决同一种问题,有多种工具时,这里面就有好有坏,有取有舍。如果你是一个学生,如果你刚工作,如果你是做后端的,建议你一定要看看 Go 语言。如果你是一个工作多年的人了,相信哪种语言对你来说也不太重要了。