欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Go学习笔记02

程序员文章站 2022-10-16 16:02:52
前言 上篇内容,介绍了如何在不同的系统上安装 Go 开发环境和部分参数的配置,也简单介绍了 package 的概念、导入方式和我对包的初始化过程的理解,关于初始化顺序的理解,可能有错误,后期会有修改,也希望大家能指出来,帮助我学习下,谢谢。 这篇内容,讲些基础的知识,比如变量的声明方式、变量的重声明 ......

前言

上篇内容,介绍了如何在不同的系统上安装 go 开发环境和部分参数的配置,也简单介绍了 package 的概念、导入方式和我对包的初始化过程的理解,关于初始化顺序的理解,可能有错误,后期会有修改,也希望大家能指出来,帮助我学习下,谢谢。

这篇内容,讲些基础的知识,比如变量的声明方式、变量的重声明和重命名区别。

变量的声明

go 语言中,变量的声明有几种方式,如:

var i,j int = 1,2
var i,j = 1,"2"
i,j,k := 1,"2",true
var (
    m int=4
    n = "golang"
    r = true
)

这里面比较特殊的是 i,j,k := 1,"2",true ,这在 go 语言里被称为短声明,它有一个特殊点,就是不能在包级作用域中使用,只能在函数或块中使用。这里涉及到我要讲的两个知识点,变量重声明重名变量

变量的重声明

什么是变量的重声明?让我们用个例子来解释吧:

var err error
n,err:=io.writestring(os.stdout,"hello world!\n")

从代码中我们看到,err 在第一行被声明了,接着我们又使用短声明的方式重新给它了值,注意这里没有变量重名即变量覆盖,发生的是变量重声明,这些代码在一个代码块中,如果你不确定,可以输出下变量地址看看是否有改变。这里我们先注意下变量重声明的前提条件,如下:

  1. 由于变量的类型在初始化时就已经确定,所以再次声明时赋予的类型必须与原本的相同,否则编译报错。

  2. 变量的声明只能发生在同一个代码块中。如果是与不同的代码块的变量重名,那么就是变量重名了,下文会介绍。

  3. 变量的重声明只有在短变量声明时才会发生,否则便已报错。

  4. 被声明的变量必须有多个,而且其中至少有一个新变量,而且不能使用 *_* 表示新变量。

上面提到了变量重名,如果你是个老鸟,那么你肯定知道,你可以略过这个例子了:

var name = "tom"
{
    name := []string{"jerry"}
    fmt.printf("%v\n",name)
}
fmt.printf("%s\n",name)

也许心细如你,发现了不同,没错这两次变量的声明发生在了不同的代码块,这次就是变量的覆盖了,好了现在我们可以总结下两者的区别了

  1. 变量的重声明必须在同一代码块中,注意是 同一代码
  2. 变量重声明是对同一变量的的多次声明,即变量只有一个。
  3. 变量重声明,不论多少次其类型始终一致,必须遵从它的次声明指定的类型。而重名变量则不存在类型一致的限制,可以是任意不同类型。
  4. 如果可重名变量所在代码存在直接或间接的嵌套关系v,那么肯定会存在“屏蔽”现象,而变量重声明不会。

数组和切片

数组(array)类型和切片(slice)类型,两者都属于集合类型,都是存储了某一类型的值,这么看貌似它们没有什么区别,先让我们通过几个简单的例子看下区别。

数组的长度在声明时就需要给定,以后对其的使用是不能改变其长度的,也就是说长度是类型的一部分。

比如[5]int与[6]int是不同的两种数组类型;然后切片的类型字面量中只有元素的类型,而没有其长度。换句话说,切片的长度可以随着其中的元素增长而增长,但不会随着减少而减少。让我们看下几个示例,如下:

// 一维数组
array1 :=[6]int
// 二维数组
array2 :=[10][20]string
// 这里还是数组,自动计算数组的长度
array3 :=[...]int{10,20,30,40}
// 切片,len()=cap()=1
slice1 :=[]int{1}
// 声明一个长度为5,但容量为10的string类型的切片,长度和容量都是可变。容量参数10可省略,那么该slice的容量跟长度一致都是5
slice3 := make([]string,5,10)
// 连续插入多个值,其实后面也是个slice
slice1=append(slice1,2,3,4)
// 从slice1第二个素开始取出两个并组成一个新的切片,slice1和slice2是共享一个底层数组的。len(slice2)=2,cap(slice2)=3
slice2:=slice1[1:3]
// 如果这里修改了slice2的第一个元素值,slice1会有变化么,想想,不确定的话可以自己尝试输出下
slice2[0]=-2
// 这里又声明了slice4,似乎与slice2有些区别,那么区别在哪呢?
slice4:=slice1[1:3:3]

看了上面的示例代码,也许对切片会有更多的疑惑,别急现在我们一一解答。

示例代码中 slice2:=slice1[1:3] 这里使用切片slice1创建了一个新切片,其长度 len(slice2)cap(slice2) 分别是2,3。为什么其容量是3呢?先解释下这个声明语句,slice1[1:3] 第一个值表示新切片开始的元素位置,这里是1,表示是从slice1的第二元素开始。第二个值表示开始的索引位置(1),加上希望包含的元素个数(2),1+2的结果就是3,所以第二个值就是3,但要注意这里是不包含第二个值代表的索引位置的值,这里是数学中前开后闭的区间。那么第二个切片的容量怎么计算的呢?由于两个切片共享同一个底层数组,底层数组的容量是4,由于slice2是从索引位置为(1)开始,那么slice2的容量就是4-1=3,所以容量就是3。由于这两个切片共享同一个底层数组,所以 slice2[0]=-2 语句是修改的底层数组的值,对应的slice1[1]位置的值也被修改。想一下下面的代码输出结果是什么?

slice1:=[]int{1,2,3,4}
slice2:=slice1[1:3]
slice2=append(slice2,5)
fmt.printf("s1:%v\n",slice1)
fmt.printf("s2:%v\n",slice2)

你想到的输出结果,跟下面一样么?

s1:[]{1,2,3,5}
s2:[]{2,3,5}

上面提到 slice2:=slice1[1:3] 中有第三个参数,新切片的容量,默认是可忽略的,此时新切片的容量是 cap(slice1)-1 。你也可以加入第三个参数,但你要记住它是不能超过底层数组的容量的,也不能小于第二个参数值否则编译报错,如 slice2:=slice1[1:3:3] ,此时新切片的容量变成了3-1=2,跟长度一致,但前提是cap(slice2)的容量不能超过底层数组的容量cap(slice1),也不能少于len(slice2)。想一下,第三个参数有什么作用呢?