前言

上篇内容,介绍了如何在不同的系统上安装 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)。想一下,第三个参数有什么作用呢?

04-14 00:03