GO语言基础(下)

简介: GO语言基础(下)

四、常用结构


1、内置函数、闭包

  1. close:主要用来关闭channel
  2. len:用来求长度,比如string、 array、 slice、 map、 channel
  3. new:用来分配内存,主要用来分配值类型,比如int、 struct。返回的是指针
  4. make:用来分配内存,主要用来分配引用类型,比如chan、 map、 slice
  5. append:用来追加元素到数组、 slice中
  6. panic和recover:用来做错误处理


闭包:一个函数和与其相关的引用环境组合而成的实体

package main
import "fmt"
func Adder() func(int) int {
  var x int//只初始化一次
  return func(delta int) int {
    x += delta
    return x
  }
}
//上述代码类似于cpp中在类中定义成员x,func函数对x进行+delta
func main() {
  var f = Adder()
  fmt.Println("1:", f(1))//0
  fmt.Println("2:", f(20))//21
  fmt.Println("3:", f(300))//321
}


2795ab0f699921dc7d1bb165ffd49194.png

2、数组与切片

数组:是同一种数据类型的固定长度的序列

数组定义: var a [len]int,比如: var a[5]int 一旦定义,长度不能变

长度是数组类型的一部分,因此, var a[5] int和var a[10]int是不同的类型

访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic

数组是值类型,因此参数传递改变的是副本的值,不会改变本身的值

数组初始化及遍历:

0dbaac557ac27f1cc4d8712266efb273.png

多维数组及遍历:

var age [5][3]int
var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
    for k2, v2 := range v1 {
        fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
    }
    fmt.Println()
}


切片定义:

切片:切片是数组的一个引用,因此切片是引用类型

切片的长度可以改变,因此, 切片是一个可变的数组

切片遍历方式和数组一样,可以用len()求长度

cap可以求出slice最大的容量, 0 <= len(slice) <= (array), 其中array是slice引用的数组

make创建切片:

slice := make([]type, len)
slice := make([]type, len, cap)

afb8db963d96073a439078923ef70715.png

切片初始化:

var slice1 []int = arr[start:end]//包含start到end之间的元素,但不包含end,[start,end)
var slice2 []int = arr[0:end]//可以简写为 var slice []int=arr[:end]
var slice3 []int = arr[start:len(arr)]//可以简写为 var slice[]int = arr[start:]
var slice4 []int = arr[0, len(arr)]//可以简写为 var slice[]int = arr[:]
Slice = slice[:len(slice)-1] //最后一个元素去掉 


7286093db01c3e5881cc72867b42bca4.png

切片的内存布局,类似C++ vector:

c4c83cbb8577f60030fc8bf5b0c30271.png

用append内置函数操作切片:

slice := make([]int, 10)
slice=append(slice, 10)//增添元素,更新
var a = []int{1, 2, 3}
var b = []int{4, 5, 6}
a = append(a, b...)//增添数组


For range 遍历切片:

for index, val := range slice {
}


切片resize:

var a = []int {1,3,4,5}
b := a[1:2]
b = b[0:3]


切片拷贝:

s1 := []int{1,2,3,4,5}
s2 := make([]int, 10)
copy(s2, s1)


string与slice:

string底层就是一个byte的数组,因此,也可以进行切片操作

str := “hello world”
s1 := str[0:5]
fmt.Println(s1)
s2 := str[5:]
fmt.Println(s2)  
s := []byte(str)//修改str
s[0] = ‘o’
str = string(s)  


string的底层布局:

54134b817fa1516bc4d5d355e9de4751.png

数组和切片的区别:

数组:类型 [n]T 表示拥有 n 个 T 类型的值的数组,指定空间大小

切片:类型 []T 表示一个元素类型为 T 的切片(动态开辟数组),不指定大小

new和make的区别:

var i *int=new(int)
*i=10
var num []int = make([]int, 10)
num[1]=10


make也是用于内存分配的,但是和new不同,它只用于chan、 map以及切片的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了


3、map

map是一种特殊的数据结构:一种元素对 (pair) 的无序集合,pair 的一个元素是 key,对应的另一个元素是 value,所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构:给定 key,对应的 value 可以迅速定位。

map是引用类型,可以使用如下声明:

var map1 map[keytype]valuetype
var map1 map[string]int


key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 stringintfloat32(64)。所以数组、切片和结构体不能作为 key ,但是指针和接口类型可以。如果要用结构体作为 key 可以提供Key()Hash() 方法,这样可以通过结构体的域计算出唯一的数字或者字符串的 key

value 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言

map引用类型 的: 内存用 make() 方法来分配

map 的初始化:

var map1 = make(map[keytype]valuetype)


你错误地使用 new() 分配了一个引用对象,你会获得一个空引用的指针

测试 map1 中是否存在 key1

if _, ok := map1[key1]; ok {
  // ...
}


map1 中删除 key1

delete(map1, key1)


注:如果 key1 不存在,该操作不会产生错误

使用 for 循环读取 map

for key, value := range map1 {
  ...
}


只想获取 key

for key := range map1 {
  fmt.Printf("key is: %d\n", key)
}


注意 map 不是按照 key 的顺序排列的,也不是按照 value 的序排列的

如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包),然后可以使用切片的 for-range 方法打印出所有的 key 和 value

package main
import (
  "fmt"
  "sort"
var (
  barVal = map[string]int{"alpha": 34, "bravo": 56, "charlie": 23,
              "delta": 87, "echo": 56, "foxtrot": 12,
              "golf": 34, "hotel": 16, "indio": 87,
              "juliet": 65, "kili": 43, "lima": 98}
)
func main() {
  fmt.Println("unsorted:")
  for k, v := range barVal {
    fmt.Printf("Key: %v, Value: %v / ", k, v)
  }
  keys := make([]string, len(barVal))
  i := 0
  for k, _ := range barVal {
    keys[i] = k
    i++
  }
  sort.Strings(keys)
  fmt.Println()
  fmt.Println("sorted:")
  for _, k := range keys {
    fmt.Printf("Key: %v, Value: %v / ", k, barVal[k])
  }
}


五、Go中包及go mod


1、go mod

实际项目开发中我们首先要在我们项目目录中用go mod命令生成一个go.mod文件管理我们项目的依赖

使用go mod命令生成一个go.mod文件

go mod init goProject


生成一个 go.mod 的文件,里面的内容是go版本,以及以后添加的包

module goProject
go 1.14


2、包

包(package)是多个Go源码的集合,一个包可以简单理解为一个存放多个.go文件的文件夹。该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包

package 包名


注意事项

  • 一个文件夹下面直接包含的文件只能归属一个package,同样一个package的文件不能在多个文件夹下。
  • 包名可以不和文件夹的名字一样,包名不能包含-符号。
  • 包名为main的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件。


3、init()初始化函数

在Go 语言程序执行时导入包语句会自动触发包内部init()函数的调用。

需要注意的是:init() 函数没有参数也没有返回值。init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。

包初始化顺序:


依赖

使用go mod命令生成一个go.mod文件

go mod init goProject


生成一个 go.mod 的文件,里面的内容是go版本,以及以后添加的包

module goProject
go 1.14


2、包

包(package)是多个Go源码的集合,一个包可以简单理解为一个存放多个.go文件的文件夹。该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包

package 包名


注意事项

  • 一个文件夹下面直接包含的文件只能归属一个package,同样一个package的文件不能在多个文件夹下。
  • 包名可以不和文件夹的名字一样,包名不能包含-符号。
  • 包名为main的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件。


3、init()初始化函数

在Go 语言程序执行时导入包语句会自动触发包内部init()函数的调用。

需要注意的是:init() 函数没有参数也没有返回值。init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。

包初始化顺序:

[外链图片转存中…(img-jR5UCw7D-1698326264819)]

[外链图片转存中…(img-OPYWhU6s-1698326264819)]

相关文章
|
18天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
29 7
|
18天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
18天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
93 71
|
17天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
100 67
|
20天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
90 62
|
22天前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
18天前
|
存储 Go
go语言中映射
go语言中映射
32 11
|
20天前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
30 12
|
19天前
|
Go 索引
go语言使用索引遍历
go语言使用索引遍历
27 9
|
23天前
|
安全 Serverless Go
Go语言中的并发编程:深入理解与实践####
本文旨在为读者提供一个关于Go语言并发编程的全面指南。我们将从并发的基本概念讲起,逐步深入到Go语言特有的goroutine和channel机制,探讨它们如何简化多线程编程的复杂性。通过实例演示和代码分析,本文将揭示Go语言在处理并发任务时的优势,以及如何在实际项目中高效利用这些特性来提升性能和响应速度。无论你是Go语言的初学者还是有一定经验的开发者,本文都将为你提供有价值的见解和实用的技巧。 ####