书接上回,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等脚本语言中,所以说白了,天下文治,必由之路,原理上大同小异,只是运使法门上略有不同,却还是有互相映照之处,下一回咱们将进入到具体变量类型的修炼,欲知更多,敬请期待。