go--变量、常量、作用域
变量
变量可以通过变量名访问。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、数组 这几种数据类型时,如果遇到变量的赋值则会拷贝一份。【值类型】