go--变量、常量、作用域

发布时间 2023-11-07 18:09:54作者: 邹邹很busy。

变量

变量可以通过变量名访问。Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

声明变量的一般形式是使用 var 关键字:

var 变量名 变量类型

也可以一次声明多个变量

var 变量名1,变量名2 变量类型
package main

import "fmt"

func main() {
   var a string = "zouzou"  // 定义了个变量 a,类型为 string,值为 邹邹
   fmt.Println(a)

   var b, c int = 1, 2 // 定义了两个变量 b 和 c,类型为 int,b 的值为 1,c 的值为 2
   fmt.Println(b, c)
}

结果

zouzou
1 2

如果没有初始化,则变量默认为零值

零值就是变量没有做初始化时系统默认设置的值。

package main

import "fmt"

func main() {

   // 声明一个变量并初始化
   var a = "zouzou"
   fmt.Println(a)

   // 没有初始化就为零值
   var b int
   fmt.Println(b)

   // bool 零值为 false
   var c bool
   fmt.Println(c)
}

结果

zouzou
0
false

零值的有

  • 数值类型(包括complex64/128)为 0

  • 布尔类型为 false

  • 字符串为 ""(空字符串)

  • 以下几种类型为 nil

var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口

类型推导

如果声明了一个变量,没有声明类型,会根据值进行类型推导

package main

import "fmt"

func main() {
   var d = true // 没有声明类型,会自动推导
   fmt.Println(d)
}

结果

true

如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式

v_name := value

例如

var intVal int 
intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明

直接使用下面的语句即可:

intVal := 1 // 此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句

intVal := 1 相等于

var intVal int 
intVal =1 

可以将 var f string = "zouzou" 简写为 f := "zouzou":

package main

import "fmt"

func main() {
   f := "zouzou" // 等价于 var f string = "zouzou"
   fmt.Println(f)
}

 还有其他的声明方式

package main

var x, y int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
   a int
   b bool
)

var c, d int = 1, 2
var e, f = 123, "hello"

//这种不带声明格式的只能在函数体中出现
//g, h := 123, "hello"

func main() {
   g, h := 123, "hello"
   println(x, y, a, b, c, d, e, f, g, h)
}

使用 := 赋值操作符

我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。

a 和 b 的类型(int 和 bool)将由编译器自动推断。

这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。

常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量的定义格式:

const 常量名 [type] = value

你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。

  • 显式类型定义: const b string = "abc"
  • 隐式类型定义: const b = "abc"
package main

import "fmt"

func main() {
   const LENGTH int = 10           // 常量 LENGTH
   const WIDTH int = 5             // 常量 WIDTH
   var area int                    // 变量
   const a, b, c = 1, false, "zou" //多重赋值

   area = LENGTH * WIDTH
   fmt.Printf("面积为 : %d", area)
   println()
   println(a, b, c)
}

结果

面积为 : 50
1 false zou

 在常量组中,如果不指定类型和值,则和上一行的类型还有值都一样

package main

import "fmt"

func main() {
   // 常量组如果不指定类型和值,则和上一行的类型的值一样
   const (
      x int = 10
      y
      a = "abc"
      b
   )
   fmt.Print(x, y, a, b)
}

结果

10 10 abc abc

iota

  • iota,特殊常量,可以认为是一个可以被编译器修改的常量。
  • iota 只能在常量组中使用
  • 不同的 const 块中互相不影响
  • 从第一行开始,每一行 iota 都会加 1

iota 可以被用作枚举值

const (
    a = iota // iota = 0
    b = iota // iota = 1,每一行 iota 加 1
    c = iota // iota = 2,每一行 iota 加 1
)

第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:

const (
    a = iota
    b
    c
)

看下面的例子

package main

import "fmt"

func main() {
   const (
      a    = iota       // 0
      b    = 10         // iota = 1
      c                 // 和上一行的值相等
      d, e = iota, iota // iota = 3,所以 d、e 都等于 3
      f    = iota       // iota = 4
   )
   fmt.Println(a, b, c, d, e, f)
}

 结果

0 10 10 3 3 4

例子二

package main

import "fmt"

func main() {
   const (
      a = iota // 0
      b        // 1
      c        // 2
      d = "ha" //独立值,iota = 3
      e        //没有值和上一行的值相等 "ha"   iota = 4
      f = 100  //iota = 5
      g        //没有值和上一行的值相等 100  iota = 6
      h = iota // 7,恢复计数
      i        // 8
   )
   fmt.Println(a, b, c, d, e, f, g, h, i)
}

 输出结果

0 1 2 ha ha 100 100 7 8

作用域

package main

import "fmt"

func main() {
   name := "zouzou" // 只在当前的 {} 里有效
   fmt.Println(name)
   if true {
      fmt.Println(name)
      age := 18 // 只在当前的 {} 里有效
      fmt.Println(age)
      name := "邹邹"
      fmt.Println(name)
   }
   println(name)
   //fmt.Println(age) 错误,因为 age 是局部变量

}

结果

zouzou
zouzou
18
邹邹
zouzou
  • 全局变量,未写在函数中的变量称为全局变量;不可以使用v1:=xx方式进行简化;可以基于因式分解方式声明多个变量;
  • 局部变量,编写在 {} 里面的变量;可以使用任意方式简化;可以基于因式分解方式声明多个变量;
package main

import "fmt"

// 全局变量(不能以省略的方式)
var school string = "北京大学" // 可以
//var school = "北京大学"   // 可以
//school := "北京大学"         // 不可以,不能简写

var ( // 全局变量
   v1 = 123
   v2 = "你好"
   v3 int
)

func main() {

   name := "zouzou" // 局部变量
   fmt.Println(name)
   if true {
      age := 18    // 局部变量
      name := "邹邹" // 局部变量
      fmt.Println(age)
      fmt.Println(name)
   }
   fmt.Println(name)
   fmt.Println(school)
   fmt.Println(v1, v2, v3)
}

结果

zouzou
18
邹邹
zouzou
北京大学
123 你好 0

赋值与内存相关

name := "zouzou"

如上代码,定义了一个变量,会在内存中开辟出一块地址,赋值给变量 name

name := "zouzou"
nickname := name

如上如果定义了一个变量 name,在定义了一个变量 nickname,将 name 赋值给 nickname。在 go 中,会将 name 的拷贝一份,如下

package main

import "fmt"

func main() {
   name := "zouzou"
   nickName := name
   fmt.Println(name, &name)         // &name 是 name 变量的内存地址
   fmt.Println(nickName, &nickName) // &nickName 是 nickName 变量的内存地址
}

结果

zouzou 0xc000010250
zouzou 0xc000010260

可以看到,我们把一个变量赋值给另一个变量后,内存地址是不一样的,因为会新开辟一块内存地址。不像 python 语言,会指向同一块内存地址

例子

package main

import "fmt"

func main() {
   name := "zouzou"
   nickName := name
   fmt.Println(name, &name)
   fmt.Println(nickName, &nickName)

   name = "daliu" // 修改了name 的值,但是内存地址还是之前的,只是在之前的内存地址上修改了值
   fmt.Println(name, &name)
   fmt.Println(nickName, &nickName) // 值类型,会拷贝一份
}

结果

zouzou 0xc000010250
zouzou 0xc000010260
daliu 0xc000010250
zouzou 0xc000010260

可以看到,当我们改变变量的值后,内存地址没有变,只是改变了内存地址里对应的数据。而赋值后的变量 nickName 是不会变的,因为是新开辟的内存地址,没有改变这个内存地址的值

注意:使用 int、string、bool、数组 这几种数据类型时,如果遇到变量的赋值则会拷贝一份。【值类型】