我们是80后

我都承认文章标题起的是越来越扯淡了。从开始的"安装Golang","解密Golang工程结构"这一类中规中矩的标题,现在已经慢慢进化成了"维多利亚的秘密","我们是80后"这一类相当无技术含义的标题。说实话,我都快编不下去了。

来一段歌词,看能否勾起你的回忆:

敢在景阳岗上穿虎皮装。 在高粱地里套母狼。左手拿刀(挲~),右手拿枪(啪~)。他就是蟑螂中的绝对偶像。敢去皇宫里翻遍御膳房。也能到你家里去扫荡。就算鞋板儿拍下敌敌畏夺命。死也要死个酷了毙。

早上无意听到这段<特务小强>的歌词,感觉一下就回到了上大学的那个时光。 不知道即将步入青年后半段的人,是不是都喜欢怀旧。我的80后记忆还停留在老北京酸奶,北京方便面,镭射录像厅,世嘉游戏机。除了二八大杠的自行车,就是路边的游戏机房。 那会的网吧还都是局域网在玩红警,CS刚出来名称换做反恐精英。沙丘还能找到,但玩最多的当属金庸群侠传。

就是在这些游戏当中,让我对计算机充满了好奇,也憧憬着如果有一天我能自己写个游戏该有多好。理想一直存在,可现实已被摧残。看着身边的90后,00后已经逐渐扛起行业的大梁,只能对着他们感叹一句:

在青年的队列里面,我们已经排在了队尾。好好干,总有一天,让我们在队尾汇合。

感慨已完,开始正题。在维密一节中,我们讲到了函数的使用方式,本节聊一聊变量作用域的事情。

变量作用域

综合之前的描述,我们可以得出一个结论:任何一种语言,都存在变量。 并由此可知,只要有变量,就会有变量作用域。 好比每个国家都有法律,而每个法律都有适用范围。最简单的例子就是中国法律管不了美国的事。所以本节聊的就是变量作用域的那些事。

在Golang当中,变量分为三类:

  • 本地局部变量
  • 全局变量
  • 参数变量

本地局部变量,顾名思义只能在本地生效的变量。在Golang当中,本地的定义是按照函数或者语义块来区分的。只要在一个函数中定义的变量,或者在一个语义块中定义的变量都是本地变量。

函数定义容易理解,例如下面的例子:

  1. func Test(){
  2. str := "This is a local var"
  3. ....
  4. }

str是定义在Test函数内部的变量,因此就是本地变量。下面则是一个语义块的示例:

  1. func Test(){
  2. for{
  3. str := "This is a local var in block"
  4. ...
  5. }
  6. }

可以看到str是定义在for这个语义块内部的,因此str也是一个本地变量。 那么本地变量的作用域在哪里呢? 哪国的法律管哪国的事,变量定义在哪里,就在当前范围内生效。

在示例一中,str定义在Test函数中,因此在Test函数内部,str都畅通无阻。出了Test函数以后,就无法使用str变量了。 同理可知,在示例二中,str定义在for循环内部,因此只能在for内部使用str。一旦出了for循环,同样也无法再访问str。

是的,你一定会问,如果出现重名变量怎么办? 首先我建议,尽量不要出现重名。 尽量不要自己给自己挖坑。 但非挖不可,那Golang会按照当前作用域优先的原则,替换变量。例如下面的例子:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. str := "local var"
  7. if true {
  8. str := "block var"
  9. fmt.Println(str)
  10. }
  11. fmt.Println(str)
  12. }

在main函数中定义了一个str变量,然后再if语义块中也定义了一个str变量。按照当前作用域优先的原则,当程序运行到if内部之后,if中的str会替代外部的str变量值。 所以首先输出"block var'。 当出了if范围之后,里面的str已经失效,外部无法访问。因此最后会使用main函数作用域的str变量,所以输出"local var"。

因此最终输出

  1. block var
  2. local var

还是那句话,代码最好别用花哨的东西,写的清清楚楚明明白白最好。一般别挖坑,要挖就挖大坑。

与本地变量相对应的就是全局变量。 只要是在函数外部定义的都属于全局变量。例如下面的例子:

  1. var g int
  2. func main(){
  3. a := 10
  4. b := 10
  5. g = a+b
  6. ...
  7. }

注意千万不能g := a+b。 因为我们说过变量的声明方式有“var"和”:="两种。如果不小心写了个" g:=a+b"。虽然对最终的结果没有影响,但意思已经改变了,变成了在main函数中重新创建了一个g变量。

虽说全局变量也是变量,但全局变量的声明只能通过var来实现。例如下面是合法的:

  1. var g int
  2. 或者
  3. var g = 40
  4. func main(){
  5. ...
  6. }

而下面则是非法的:

  1. g := 40
  2. func main(){
  3. }

不要问什么了,规定就是规定,遵守就好。全局变量的作用域当然就是全局了,但这个全局指的是package内。 在当前package内都可以随意访问,如果想让其他package访问,那么需要遵从Golang的原则,首字母大写。这样才能让其他Package访问到。

如果遇到同名问题怎么样? Golang同样会按照作用域优先原则处理,例如下面的例子:

  1. var g = 40
  2. func main(){
  3. a := 10
  4. b := 10
  5. g := a+b
  6. ...
  7. }

注意,在main函数中,同样存在一个g变量,但这个g变量是通过":="声明出来的。 因此按照作用域优先原则,此时此刻的g=30。全局的g=40。 如果这方面感觉不理解,写两句代码,run一下立马见分晓。

最后一种是参数变量。参数变量是一种特殊的变量,既不属于本地变量,也不属于全局变量(有点废话了,如果属于就不会单列出来了)。 其介于两者之间,作用域是函数范围,先看下面的例子:

  1. func Test(str string){
  2. i := 10
  3. ....
  4. }

通过上面的分析可知,i属于本地变量,而str则属于参数变量。 与本地变量相同的一点时,其作用域相同,都是函数范围。 但不同的地方有两处:

  • 参数变量赋值是在外部完成的,联想一下<维密>那节,我们讲解函数时,如何给参数赋值。就可以知道赋值的动作是在调用时完成的。
  • 参数变量可以声明之后不使用。这点尤为明显,如果声明了一个本地变量,但自始至终都没有用到。编译时会提示删除这个变量。而参数变量,无论函数内部有没有用到,都不会提示。
    所以可以向对待本地局部变量那样对待参数变量,本地局部变量适用的法则,参数变量同样适用。

写完变量,看了看内容不多。因此再多写一点,反正没有出版社约稿,也就不会受到大纲的约束,想到哪里写到哪里。

下面进入Golang常用类型的使用场景

String

在每种编程开发语言种,字符串都是内置的一种数据类型,原因就是因为它太重要了。前面也提到过一嘴,Golang的字符串本质是字符数组([]byte)。

创建字符串很简单,上面用过很多次了:

  1. var greeting = "Hello world!"
  2. 或者
  3. greeting := "Hello world!"

Golang默认使用UTF-8编码处理字符串,所以不用担心汉字编码的问题。作为一种现代语言,Golang不限制字符串的长度,理论上只要内存有多大,字符串就可以有多大。但还是本着"no zuo no die"的理念,别放那么长的字符串,没人喜欢在代码中读小说。

Golang专门有一个strings的package(https://golang.org/pkg/strings/ )来处理各种常见的string问题。例如查找,拼接,判断,大小写转换和替换,拷贝等等常见功能。

String很重要,但真的没什么可讲的。绝大多数的问题都可以通过strings库来解决,如果有问题,自行看一下库文档吧。 虽说是本系列是个入门教程,但事无巨细都写完,很不现实。 如果写完了,那就变成Golang中文文档了。

所以String这一小节,记住有一个strings库就够了。

如果你是80后,真心感叹你还能有如此强的求知欲。 如果你非80后,好好珍惜眼前的日子吧。 该玩就玩,该挥霍就挥霍。别等哪天将发际线熬到了耳后,才后悔当初没有好好泡妹子。