写点什么

牛刀小试基本语法,Go lang1.18 入门精炼教程,由白丁入鸿儒,go lang 基本语法和变量的使用 EP02

  • 2022 年 8 月 05 日
  • 本文字数:4606 字

    阅读完需:约 15 分钟

牛刀小试基本语法,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本语法和变量的使用EP02

书接上回,Go lang1.18 首个程序的运行犹如一声悠扬的长笛,标志着并发编程的 Go lang 巨轮正式开始起航。那么,在这艘巨轮之上,我们首先该做些什么呢?当然需要了解最基本的语法,那就是基础变量的声明与使用。

变量的声明与使用

变量是什么玩意?是具体的数据被内存存储之后内存地址的名称。说白了就是内存中的门牌号,在 go lang 中声明变量有很多种方式,相对严谨的:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name int    name = 1      f.Println(name)    }
1
复制代码


这里用 var 关键字声明变量 name,变量名称可以是字母或下划线开头,由一个或多个字母、数字、下划线组成。随后指定数据类型,这里是整形,接着进行赋值操作,如果没有赋值动作,go lang 会自动填充一个默认值:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name int      f.Println(name)    }
0
复制代码


相对简单一点的声明方式:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name = 1      f.Println(name)    }
复制代码


如果一个变量有一个初始值,go lang 将自动能够使用初始值来推断该变量的类型。因此,如果变量具有初始值,则可以省略变量声明中的类型,也就是说一个,你得提前让 go lang 知道这个变量的数据类型,无论是通过那种方式。


最后,类似 Python 中海象操作符的声明方式:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      name := 1      f.Println(name)    }
复制代码


海象操作符这样的声明方式可以不使用 var 关键字,事实上,它更像是一个连贯操作,既声明又赋值,算得上是赋值表达式。


但需要注意已经声明过的(多个变量同时声明时,至少保证一个是新变量),否则会导致编译出错:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      name = 1      //重复赋值      name := 2      f.Println(name)    }
复制代码


程序返回:


command-line-arguments  # command-line-arguments  .\test.go:9:2: undefined: name  > Elapsed: 1.097s  > Result: Error
复制代码


另外,海象操作符声明只能被用在方法里面,而不可以用于全局变量的声明与赋值。


如果不想手动一个一个赋值,也可以进行多变量赋值的操作:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name1, name2, name3 = 1, "2", false      f.Println(name1)    f.Println(name2)    f.Println(name3)    }
1 2 false
复制代码


变量永远都必须先声明才能使用,这是放之四海而皆准的原则,不同于 Python 或者 Ruby,go lang 是静态语言,要求变量的类型和赋值的类型必须一致:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name = "你好"      name = 1      f.Println(name)    }
复制代码


这里会报类型异常的错误:


command-line-arguments  # command-line-arguments  .\test.go:11:9: cannot use 1 (untyped int constant) as string value in assignment  > Elapsed: 0.561s  > Result: Error
复制代码


最后,声明了变量就需要使用,如果不用,那么声明的意义在哪儿呢?


func main() {     var a string = "abc"     fmt.Println("hello, go lang")  }
复制代码


编译后会报异常:


a declared and not used
复制代码

变量内存地址、占用空间大小和交换赋值

任何声明的变量都在内存中有自己的地址,我们可以通过 &关键字将其获取出来:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name = "你好"      f.Println("name的内存地址是", &name)    }
复制代码


程序返回:


name的内存地址是 0xc00003c250
复制代码


和 Python 的内存管理机制不同,go lang 会将相同值的变量指向不同的内存地址:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      //声明变量      var name = "你好"      a := name      f.Println("name的内存地址是", &name)      f.Println("a的内存地址是", &a)    }
复制代码


程序返回:


name的内存地址是 0xc00003c230  a的内存地址是 0xc00003c240
复制代码


但地址的范围是相似的,这样更方便同类型同值的变量回收,有点类似“网段”的概念。


我们也可以通过 unsafe 包的 Sizeof 方法来获取变量具体在内存中占用多少空间:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    import u "unsafe"      func main() { // 声明 main 主函数入口      a := 100    f.Println("a的地址:", &a)      f.Println("占用内存:", u.Sizeof(a))    }
复制代码


程序返回:


a的地址: 0xc0000aa058  占用内存: 8
复制代码


如果我们想交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      b, a := 1, 2      f.Println(&b, &a)      b, a = a, b      f.Println(b, a)      f.Println(&b, &a)    }
复制代码


程序返回:


0xc00012c058 0xc00012c070  2 1  0xc00012c058 0xc00012c070
复制代码


由此我们可以发现,值交换了,但内存地址并未改变,可以理解为旧瓶装新酒。交换赋值的底层逻辑也和 Python 一样,需要有第三个隐藏变量来做值的传递。


另外,golang 当中也支持匿名变量,也就是说对于我们不需要的返回值或者变量,我们可以不用额外定义一个变量去接收。否则没有用处,还会报错:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      a, _ := 1, 2      f.Println("a = ", a) // 1    }
复制代码

常量 constant

常量(constant)表示不变的值。在程序运行时,不会被代码逻辑修改。比如数学上的圆周率、自然常数 e 等等:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      const value int = 1    // value = 100  // 常量是不允许被修改的    f.Println("value = ", value)    }
复制代码


这里通过 const 关键字来代替 var 关键字来声明常量,和 JavaScript 语法一致。


常量声明也可以被用于枚举场景,就是所谓的常量组:


const (      Unknown = 0      Female = 1      Male = 2  )
复制代码


在常量声明表达式中,我们也可以用 iota 关键字进行动态声明:


package main // 声明 main 包    import f "fmt" // 导入 fmt 包,打印字符串时需要用到    func main() { // 声明 main 主函数入口      // const来定义枚举类型    const (      // 可以在const()中添加一个关键字iota, 每行的iota都会累加1, 第一行的iota默认是0      a = iota + 1 // iota = 0      b            // iota = 1      c            // iota = 2    )      f.Println("a = ", a) // 1    f.Println("b = ", b) // 2    f.Println("c = ", c) // 3    }
复制代码

变量作用域

变量的作用域可以理解为可访问指定变量的程序的某个范围。可以在类,方法,循环等中定义变量。像 C / C ++一样,在 Golang 中,所有的标识符都是词法(或静态)作用域,即变量的作用域可以在编译时确定,也就是说,和 Python 不一样的是,Go lang 是具备块作用域的:


//局部变量  package main  import "fmt"    //主函数  func main() {        //从这里开始主函数的局部作用域      //主函数内的局部变量      var myvariable1, myvariable2 int = 69, 145        // 显示变量的值      fmt.Printf("myvariable1 变量的值 : %d\n", myvariable1)        fmt.Printf("myvariable2 变量的值 : %d\n", myvariable2)    } // 此处主要函数的局部作用域结束
复制代码


在方法或块中声明的变量称为局部变量,这些不能在函数或块之外访问。这些变量也可以在函数内的 for,while 语句等内部声明,但是,这些变量可以由函数内的嵌套代码块访问,这些变量也称为块变量。


如果在同一作用域中用相同的名称声明两次这些变量,则会出现编译时错误。函数执行结束后,这些变量将不存在。在循环外声明的变量也可以在嵌套循环内访问。这意味着方法和所有循环都可以访问全局变量。局部变量可被循环访问,并在该函数内执行函数。在循环体内声明的变量对循环体外不可见。


除此以外,我们可以在程序内定义全局变量:


//全局变量  package main  import "fmt"    // 全局变量声明  var myvariable1 int = 100    func main() {        // 主函数内部的局部变量      var myvariable2 int = 200        //显示全局变量      fmt.Printf("全局变量 myvariable1 的值是 : %d\n", myvariable1)        //显示局部变量      fmt.Printf("局部变量 myvariable2 的值是 : %d\n", myvariable2)        //调用函数      display()    }    func display() {      // 显示全局变量      fmt.Printf("全局变量 myvariable1 的值是 : %d\n", myvariable1)    }
复制代码


在函数或块之外定义的变量称为全局变量,这些变量在程序的整个生命周期中都可用。


最后,go lang 也有系统的内置作用域,也就是内置的关键字变量,所以我们声明变量的时候,不能和系统关键字重名,否则系统就不知道到底该调用那个作用域的变量了:


var和const :变量和常量的声明  var varName type  或者 varName : = value  package and import: 导入  func: 用于定义函数和方法  return :用于从函数返回  defer someCode :在函数退出之前执行  go : 用于并行  select 用于选择不同类型的通讯  interface 用于定义接口  struct 用于定义抽象数据类型  break、case、continue、for、fallthrough、else、if、switch、goto、default 流程控制  chan用于channel通讯  type用于声明自定义类型  map用于声明map类型数据  range用于读取slice、map、channel数据
复制代码

结语

变量是一切逻辑的基础,没有变量就不可能有运算、判断以及相关业务逻辑。如果进行类比的话,变量操作就是一门功夫的内功心法,只有掌握了内功才能用内力催动招式,同样地,掌握一门内功就可以举一反三,触类旁通,君不见 go lang 中使用的系统关键字也都会出现在诸如 Python、Javascript 等脚本语言中,所以说白了,天下武功,殊途同归,原理上大同小异,只是运使法门上略有不同,却还是有相互映照之处,下一回我们将进入到具体变量类型的修炼,欲知更多,敬请期待。

发布于: 刚刚阅读数: 3
用户头像

专注技术,凝聚意志,解决问题 v3u.cn 2020.12.21 加入

还未添加个人简介

评论

发布
暂无评论
牛刀小试基本语法,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本语法和变量的使用EP02_golang_刘悦的技术博客_InfoQ写作社区